首页 > 编程语言 >java线程:互斥锁与读写锁

java线程:互斥锁与读写锁

时间:2023-11-08 16:06:57浏览次数:30  
标签:java readLock lock 读写 互斥 线程 import


两种互斥锁机制:
1、synchronized
2、ReentrantLock
ReentrantLock是jdk5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,而且采用ReentrantLock的方式更加面向对象,也更加灵活,网上有很多关于对比两者锁方式的文章,这里就不多口舌了,大家baidu、google一下就水落石出了。在本博客中也写关于这两种锁方式实现的经典例子《生产者消费者》。


关于读写锁,用语言解释不如直接用代码诠释,以下通过两个例子讲述读写锁以及读写锁的使用:

例子1:
import java.util.HashMap;  
import java.util.Map;  
import java.util.concurrent.locks.ReadWriteLock;  
import java.util.concurrent.locks.ReentrantReadWriteLock;  
  
/** 
 * @author amber2012 
 *  
 * 读写锁:ReadWriteLock 
 *  
 * 在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题。比如在一个线程读取数据的时候,另外一个线程在 
 * 写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的 
 * 不一致性。 
 *  
 * 这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这 
 * 样是可以解决这样以上的问题,但是效率却大打折扣了。因为在真实的业务场景中,一份数据,读取数据的操作次数通常高 
 * 于写入数据的操作,而线程与线程间的读读操作是不涉及到线程安全的问题,没有必要加入互斥锁,只要在读-写,写-写期 
 * 间上锁就行了。 
 *  
 * 对于这种情况,读写锁则最好的解决方案! 
 *  
 * 读写锁的机制: 
 *      "读-读"不互斥 
 *      "读-写"互斥 
 *      "写-写"互斥 
 *  
 * 即在任何时候必须保证: 
 *      只有一个线程在写入; 
 *      线程正在读取的时候,写入操作等待; 
 *      线程正在写入的时候,其他线程的写入操作和读取操作都要等待; 
 *  
 * 以下是一个缓存类:用于演示读写锁的操作:重入、降级 
 */  
public class CachedData {  
      
    // 缓存都应该是单例的,在这里用单例模式设计:  
    private static CachedData cachedData = new CachedData();  
    private final ReadWriteLock lock = new ReentrantReadWriteLock();//读写锁  
    private Map<String, Object> cache = new HashMap<String, Object>();//缓存  
      
    private CachedData(){  
    }  
      
    public static CachedData getInstance(){  
        return cachedData;  
    }  
      
    // 读取缓存:  
    public Object read(String key) {  
        lock.readLock().lock();  
        Object obj = null;  
        try {  
            obj = cache.get(key);  
            if (obj == null) {  
                lock.readLock().unlock();  
                // 在这里的时候,其他的线程有可能获取到锁  
                lock.writeLock().lock();  
                try {  
                    if (obj == null) {  
                        obj = "查找数据库"; // 实际动作是查找数据库  
                        // 把数据更新到缓存中:  
                        cache.put(key, obj);  
                    }  
                } finally {  
                    // 当前线程在获取到写锁的过程中,可以获取到读锁,这叫锁的重入,然后导致了写锁的降级,称为降级锁。  
                    // 利用重入可以将写锁降级,但只能在当前线程保持的所有写入锁都已经释放后,才允许重入 reader使用  
                    // 它们。所以在重入的过程中,其他的线程不会有获取到锁的机会(这样做的好处)。试想,先释放写锁,在  
                    // 上读锁,这样做有什么弊端?--如果这样做,那么在释放写锁后,在得到读锁前,有可能被其他线程打断。  
                    // 重入————>降级锁的步骤:先获取写入锁,然后获取读取锁,最后释放写入锁(重点)  
                    lock.readLock().lock();   
                    lock.writeLock().unlock();  
                }  
            }  
        } finally {  
            lock.readLock().unlock();  
        }  
        return obj;  
    }  
}
例子2:
import java.util.Map;  
import java.util.TreeMap;  
import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReadWriteLock;  
import java.util.concurrent.locks.ReentrantReadWriteLock;  
  
import javax.xml.crypto.Data;  
  
/** 
 * @author amber2012 
 *  
 * jdk文档中关于ReentrantReadWriteLock类使用的一个很好的例子,以下是具体的介绍: 
 *  
 * 在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 
 * 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下 
 * 是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。  
 */  
public class RWDictionary {  
  
    private final Map<String, Data> map = new TreeMap<String, Data>();  
    private final ReadWriteLock rwl = new ReentrantReadWriteLock();  
    private final Lock readLock = rwl.readLock();  
    private final Lock writeLock = rwl.writeLock();  
  
    public Data get(String key) {  
        readLock.lock();  
        try {  
            return map.get(key);  
        } finally {  
            readLock.unlock();  
        }  
    }  
  
    public String[] allKeys() {  
        readLock.lock();  
        try {  
            return (String[]) map.keySet().toArray();  
        } finally {  
            readLock.unlock();  
        }  
    }  
  
    public Data put(String key, Data value) {  
        writeLock.lock();  
        try {  
            return map.put(key, value);  
        } finally {  
            writeLock.unlock();  
        }  
    }  
  
    public void clear() {  
        writeLock.lock();  
        try {  
            map.clear();  
        } finally {  
            writeLock.unlock();  
        }  
    }  
}





标签:java,readLock,lock,读写,互斥,线程,import
From: https://blog.51cto.com/u_809530/8255870

相关文章

  • 《java解惑》——续字符串之谜
    1.字符串替换:问题:下面这段程序把类全路经名中的"."替换为"/",代码如下:1.packagecom.javapuzzlers;2.3.publicclassTest{4.5.publicstaticvoidmain(String[]args){6.class.getName().replaceAll(".","/")+".class&......
  • java枚举使用
    Java中的枚举类型采用关键字enum来定义,从jdk1.5才有的新类型,所有的枚举类型都是继承自Enum类型。要了解枚举类型,建议大家先打开jdk中的Enum类简单读一下,这个类里面定义了很多protected方法,比如构造函数,如果要使用这些方法我们可以把枚举类型定义到当前......
  • Linux安装Java环境变量及配置分配用户权限
    1wget安装yum-yinstallwget2.下载wget--no-cookies--no-check-certificate--header"Cookie:gpw_e24=http%3A%2F%2Fwww.oracle.com%2F;oraclelicense=accept-securebackup-cookie""http://download.oracle.com/otn-pub/java/jdk/8u141-b15/336fa29ff2bb......
  • 《java解惑》--表达式之谜
    《java解惑》是Google公司的首席Java架构师JoshuaBloch继《Effectivejava》之后有一力作,专门揭示了很多java编程中意想不到的疑惑,很多有多年工作经验的java开发人员在看完本书之后甚至都怀疑自己会不会写java程序,本系列博客主要记录在读《java解惑》中的经典例子以及原因分析。1......
  • Java中用引号创建String对象和用构造函数的区别
    创建一个String对象一般有以下两种方式:Stringstr1="abcd";Stringstr2=newString("abcd");这两种方式有什么区别呢?我们可以通过下面两个小例子来说明.Example1:Stringa="abcd";Stringb="abcd";System.out.println(a==b);//True......
  • JAVA Date 时间与时间相差计算
    JAVADate时间与时间相差计算Datedate=newDate("2014/1/1018:20");Datedate2=newDate("2014/1/113:5");longtemp=date2.getTime()-date.getTime();//相差毫秒数longhours=temp/1000/3600;//相差小时......
  • 非严格模式下JavaScript语句中“this”默认指向全局对象(window)
    请阅读以下代码varobj={};obj.log=console.log;obj.log.call(console,this);该代码在浏览器中执行,输出的日志结果是什么?obj.log.call(console,this)=console.log(this)。this这里指window,所以最后的表达式是console.log(window)这道题看似在考this的绑定问题,实际......
  • JavaScript权威基础语法教程讲解大全
    JavaScriptJS基础权威语法教程讲解大全https://developer.mozilla.org/zh-CN/docs/Web/JavaScript参考、来源:《爬虫7期:爬虫&逆向7期-第1章-爬虫&逆向7期-1.32-javascript入门_02.mp4》2:28:40......
  • Java中的传值和传引用
    三年以前读研究生的时候,就因为传值和传引用的问题给自己挖过一个坑情景是,我将matlab代码翻译到java中最后计算的结果偏差超过了限差的范围,也超过了java中数据截断误差的范围。经过最后的排查发现,在计算的过程中,一个不应该改变的值被我以引用的形式传入了方法中,方法内部对值进行......
  • 七、Java集合
    一、集合概述集合的特点如下:动态大小:集合可以根据需要动态调整大小,不像数组需要提前指定大小。灵活性:集合提供了各种不同类型的数据结构和容器,例如列表、集、映射、队列等,以满足不同的存储和操作需求。高效性:Java集合框架中的实现类经过优化,提供高效的插入、删除和查找......