java锁:synchronized、ReadWriteLock、ReentrantReadWriteLock*
2016-12-28 15:13
429 查看
一、先读这篇文章,了解synchronized:Java线程同步:synchronized锁住的是代码还是对象
synchronizied,默认是synchronized(this),括号里的内容可以看成是锁。
把锁当成对象看待,在类C中加锁的代码块A,能锁住代码块A的锁有很多,new出C类的对象c1、c2,默认就是synchronized(c1){A}、synchronized(c2){A},在对象c1中,获得了c1的锁才能执行代码。在对象c2中,获得了c2的锁才能执行代码。相同的代码块A,在不同的对象c1、c2中,是可以同时执行的,但是在对象c1中,A是不能执行的(c2也同理)。
在类C中加锁的代码块A,synchronized(C.class){A},这时锁是C的class对象,相当于在一个jvm中,只有一个C的class对象,所以这时候就是全局锁了,在整个jvm中,代码A只能有一个并发。
加锁的代码是会影响并发的,所以加锁的内容能缩小就尽量缩小。
如果在一个对象中,有多个synchronized代码块A,B,C,则A现在正被锁着,B和C也会被同时锁住。
二、synchronized加锁造成的问题
对象的方法中一旦加入synchronized修饰,则这个对象任何时刻只能有一个线程访问synchronized修饰的方法。假设有个数据对象拥有写方法与读方法,多线程环境中要想保证数据的安全,需对该对象的读写方法都要加入 synchronized同步块。这样任何线程在写入时,其它线程无法读取与改变数据;如果有线程在读取时,其他线程也无法读取或写入。这种方式在写入操作远大于读操作时,问题不大,而当读取远远大于写入时,会造成性能瓶颈,因为此种情况下读取操作是可以同时进行的,而加锁操作限制了数据的并发读取。这就引出了ReadWriteLock,读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!
但是ReadWriteLock是个接口,ReentrantReadWriteLock是他的实现类,ReentrantReadWriteLock特性如下:
ReentrantReadWriteLock和synchronized一样,不同的对象是不同的锁,一个对象产生的锁,只能本身的对象。
重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想。
WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的持有。反过来ReadLock想要升级为WriteLock则不可能。
ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量。
参考:ReentrantReadWriteLock读写锁的使用
三、ReentrantReadWriteLock实例
ReentrantReadWriteLock实现了ReadWriteLock,使用ReentrantReadWriteLock,只需要在对象O(或者某个数据结构)中定义ReentrantReadWriteLock对象即可,然后调用lock或者unlock,即可对对象O实现加锁操作。
四、悲观锁乐观锁
上面的锁都是悲观锁
乐观锁即我先对num操作,操作完了之后,在判断num是否有变化,没变化,则说明没有别的线程在对num操作,那我就可以把我操作后的值赋值给num了。
synchronizied,默认是synchronized(this),括号里的内容可以看成是锁。
把锁当成对象看待,在类C中加锁的代码块A,能锁住代码块A的锁有很多,new出C类的对象c1、c2,默认就是synchronized(c1){A}、synchronized(c2){A},在对象c1中,获得了c1的锁才能执行代码。在对象c2中,获得了c2的锁才能执行代码。相同的代码块A,在不同的对象c1、c2中,是可以同时执行的,但是在对象c1中,A是不能执行的(c2也同理)。
在类C中加锁的代码块A,synchronized(C.class){A},这时锁是C的class对象,相当于在一个jvm中,只有一个C的class对象,所以这时候就是全局锁了,在整个jvm中,代码A只能有一个并发。
加锁的代码是会影响并发的,所以加锁的内容能缩小就尽量缩小。
如果在一个对象中,有多个synchronized代码块A,B,C,则A现在正被锁着,B和C也会被同时锁住。
二、synchronized加锁造成的问题
对象的方法中一旦加入synchronized修饰,则这个对象任何时刻只能有一个线程访问synchronized修饰的方法。假设有个数据对象拥有写方法与读方法,多线程环境中要想保证数据的安全,需对该对象的读写方法都要加入 synchronized同步块。这样任何线程在写入时,其它线程无法读取与改变数据;如果有线程在读取时,其他线程也无法读取或写入。这种方式在写入操作远大于读操作时,问题不大,而当读取远远大于写入时,会造成性能瓶颈,因为此种情况下读取操作是可以同时进行的,而加锁操作限制了数据的并发读取。这就引出了ReadWriteLock,读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!
但是ReadWriteLock是个接口,ReentrantReadWriteLock是他的实现类,ReentrantReadWriteLock特性如下:
ReentrantReadWriteLock和synchronized一样,不同的对象是不同的锁,一个对象产生的锁,只能本身的对象。
重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想。
WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的持有。反过来ReadLock想要升级为WriteLock则不可能。
ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量。
参考:ReentrantReadWriteLock读写锁的使用
三、ReentrantReadWriteLock实例
ReentrantReadWriteLock实现了ReadWriteLock,使用ReentrantReadWriteLock,只需要在对象O(或者某个数据结构)中定义ReentrantReadWriteLock对象即可,然后调用lock或者unlock,即可对对象O实现加锁操作。
package com.sf.log_gen; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class LockTest { public static void main(String[] args) { Count count = new Count(); new Thread(new Write(count)).start(); // 因为是不同的对象,所以这里立马就能获得ReadLock System.out.println(new Count().get()); // 针对同一对象to,因为子线程先获得了对象to的WriteLock(也有可能主线程先获得,主线程最好sleep(100L)), // 所以这里ReadLock就需要等WriteLock释放才能获得 System.out.println(count.get()); } } class Write implements Runnable { Count count = null; public Write(Count count) { this.count = count; } public void run() {// 在这个线程中先获得了writeLock this.count.lock(); this.count.add(); this.count.unlock(); } } class Count { private ReadWriteLock rwl = new ReentrantReadWriteLock(); int num = 0; public void add() { try { Thread.sleep(4000L); } catch (InterruptedException e) { e.printStackTrace(); } num++; } public int get() {// get方法如果不加锁,则所有线程可以直接读取 this.rwl.readLock().lock(); int ret = num; this.rwl.readLock().unlock(); return ret; } public void lock() { this.rwl.writeLock().lock(); } public void unlock() { this.rwl.writeLock().unlock(); } }
四、悲观锁乐观锁
上面的锁都是悲观锁
乐观锁即我先对num操作,操作完了之后,在判断num是否有变化,没变化,则说明没有别的线程在对num操作,那我就可以把我操作后的值赋值给num了。
相关文章推荐
- Java多线程——锁(Synchronized、Lock、ReentrantLock、ReadWriteLock、ReentrantReadWriteLock)
- java多线程基础---synchronized与ReentrantReadWriteLock的介绍和比较
- java多线程基础---synchronized与ReentrantReadWriteLock的介绍与比较
- Java多线程synchronized、ReentrantLock、ReentrantReadWriteLock 和StampedLock 的对比
- java类库的阅读笔记_jdk1.7.0_40_java.util.concurrent.locks.ReentrantReadWriteLock
- 《深入浅出 Java Concurrency》—锁机制(九) 读写锁 (ReentrantReadWriteLock) (2)
- 深入浅出 Java Concurrency (13): 锁机制 part 8 读写锁 (ReentrantReadWriteLock) (1)
- Synchronized与ReentrantReadWriteLock性能比较
- Java-ReentrantReadWriteLock的简单例子
- 深入浅出 Java Concurrency (14): 锁机制 part 9 读写锁 (ReentrantReadWriteLock) (2)
- Java多线程(十)之ReentrantReadWriteLock深入分析
- Java多线程系列--“JUC锁”08之 共享锁和ReentrantReadWriteLock
- Java 并发问题的处理神器:ReentrantReadWriteLock
- java 读写锁 , 官方自带示例读解,ReentrantReadWriteLock
- java 共享锁和ReentrantReadWriteLock
- 《深入浅出 Java Concurrency》—锁机制(八) 读写锁 (ReentrantReadWriteLock) (1)
- 聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁
- ReentrantReadWriteLock & ReentrantLock & Synchronized 区别
- 13、java锁之读写锁ReentrantReadWriteLock.ReadLock与ReentrantReadWriteLock.WriteLock
- java 中 ReentrantReadWriteLock的读锁和写锁的使用