微前端架构如何改变企业的开发模式与效率提升
655
2022-11-26
ReentrantReadWriteLock读写锁的使用1
本文可作为传智播客《张孝祥-Java多线程与并发库高级应用》的学习笔记。
一个简单的例子
两个线程,一个不断打印a,一个不断打印b
public class LockTest { public static void main(String[] args){ final Outputer outputer = new Outputer(); new Thread(new Runnable(){ @Override public void run() { while(true){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); } } }).start(); new Thread(new Runnable(){ @Override public void run() { while(true){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); }//a的数量与b的数量一致 } }).start(); } static class Outputer{ public void output(String name){ int len = name.length(); try{ for(int i=0;i 最后的部分结果 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 为什么会这样? 很简单,在输出b的时候,还没有输出完,a线程(打印a的那个线程)已经抢到了控制权,开始打印a,等a线程将a输出完后,并且打印了一个回车后,b线程才抢回系统控制权,打印它上一次最后剩下的一个b。 要解决上面的问题很简单: static class Outputer{ public synchronized void output(String name){ int len = name.length(); //..... } } 这样一来,我们就保证了Outputer类里的output方法是原子性的,不会有两个线程同时执行它。 就上面的例子而言我们是否还有更好的方法呢? 有。 java5中提供了一种更加面向对象的技术类解决多线程之间的互斥问题-----锁。 java.util.concurrent.locks Interface Lock 锁技术的核心就是Lock及它的实现类。 基本锁 ******************************************* ******************************************* 以下为2016您3月21日补充 既然都说到锁了,我们就看上java.util.concurrent下都有什么东西 首先concurrent下有两个子包 atomic与locks atomic包里面主要是对基本数据类型如int,float,boolean等的原子封装 lock包是我们今天要说的 OK有3个接口 首先我先说明,这3个接口之间并没有继承的关系 Lock与ReadWriteLock都是锁,可以实现线程的互斥,只是ReadWriteLock可以更进一步的实现读与读不互斥(更多的资料,见下文) 上面的readlock与writelock分别是ReentrantReadWriteLock的两个静态内部类 Condition呢,上面的Lock实现了线程的互斥,但是我们还得实现线程的通信呀,那就是condition ReadWriteLock没有这个方法的 关于condition,可参见拙作 聊聊condition 以上为2016您3月21日补充 ******************************************* ******************************************* 上面的例子如果使用锁,代码如下 static class Outputer{ Lock lock = new ReentrantLock(); public void output(String name){ int len = name.length(); lock.lock(); //标识1 try{ for(int i=0;i 线程a执行到上面代码的标识1处加锁,当线程a在输出字符a时,线程b也执行到了标识1处。此时线程b是不能获得锁的。它被阻塞到标识1处,直到线程a打印完之后在标识2处释放了锁。(线程a线程b共用一把锁,也就是Lock lock = new ReentrantLock()) 另外为什么标识2出的释放锁放到了finally里,大家应该明白了吧。 读与写 上面的问题中output的主体(len是方法内部的局部变量,为每个线程自有,互不干涉)被全部互斥,它保证了任何时候,都只有一个线程执行标识1与标识2直接的代码。 但是我们得意识到:对共有数据的操作,基本可以分为两类,读与写。 对共有资源操作的时候,我们应该遵循三大准则: 1 当一个线程对资源进行写操作的时候,别的线程既不能对资源读也不能对资源写。 2 当一个线程对资源进行读操作的时候,别的线程不能对资源写。 3 当一个线程对资源进行读操作的时候,别的线程能对资源读。 一二准则保证了系统的正确性。第三准则能提高系统的性能。 毕竟多个线程对资源进行读操作是可以的。 看下面这个既有读又有写的例子。 public class ReadWriteLockTest { public static void main(String[] args) { final Queue3 q3 = new Queue3(); for(int i=0;i<3;i++) { new Thread(){ public void run(){ while(true){ q3.get(); } } }.start(); new Thread(){ public void run(){ while(true){ q3.put(new Random().nextInt(10000)); } } }.start(); } }}class Queue3{ private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。 public void get(){ try { System.out.println(Thread.currentThread().getName() + " be ready to read data!"); Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName() + "have read data :" + data); } catch (InterruptedException e) { e.printStackTrace(); } } public void put(Object data){ try { System.out.println(Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep((long)(Math.random()*1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " have write data: " + data); } catch (InterruptedException e) { e.printStackTrace(); } }} 结果如下 Thread-0 be ready to read data! Thread-1 be ready to write data! Thread-2 be ready to read data! Thread-3 be ready to write data! Thread-4 be ready to read data! Thread-5 be ready to write data! Thread-0have read data :null Thread-0 be ready to read data! Thread-3 have write data: 5280 Thread-3 be ready to write data! Thread-1 have write data: 5839 Thread-1 be ready to write data! Thread-4have read data :5839 我们可以看到 读中有写 写中有写 写中有读 完全乱套了。 我们试试个两个方法加上synchronized 结果如下 Thread-0 be ready to read data! Thread-0have read data :null Thread-5 be ready to write data! Thread-5 have write data: 7931 Thread-5 be ready to write data! Thread-5 have write data: 9564 Thread-5 be ready to write data! Thread-5 have write data: 1203 Thread-5 be ready to write data! Thread-5 have write data: 8870 Thread-4 be ready to read data! Thread-4have read data :8870 Thread-3 be ready to write data! Thread-3 have write data: 9334 Thread-3 be ready to write data! Thread-3 have write data: 2680 Thread-3 be ready to write data! Thread-3 have write data: 9948 Thread-3 be ready to write data! Thread-3 have write data: 375 Thread-2 be ready to read data! 读与写完全互斥,读的时候不写,写的时候不读。满足一二准则。 读写锁 为了实现准则三,在java5中的出现了读写锁。 java.util.concurrent.locks Interface ReadWriteLock ReadWriteLock有两个方法 Lock readLock() Returns the lock used for reading. Lock writeLock() Returns the lock used for writing. 得到两种锁后,就可以调用锁的lock与unlock方法了。 一般使用它的子类ReentrantReadWriteLock来产生ReadWriteLock 其签名如下: public class ReentrantReadWriteLock extends Object implements ReadWriteLock, Serializable 看看使用方法 class Queue3{ private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。 ReadWriteLock rwl = new ReentrantReadWriteLock(); public void get(){ rwl.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to read data!"); Thread.sleep(20); System.out.println(Thread.currentThread().getName() + " have read data :" + data); } catch (InterruptedException e) { e.printStackTrace(); }finally{ rwl.readLock().unlock(); } } public void put(Object data){ rwl.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep(20); this.data = data; System.out.println(Thread.currentThread().getName() + " have write data: " + data); } catch (InterruptedException e) { e.printStackTrace(); }finally{ rwl.writeLock().unlock(); } }} 结果如下 Thread-5 have write data: 7329 Thread-0 be ready to read data! Thread-0 have read data :7329 Thread-1 be ready to write data! Thread-1 have write data: 1361 Thread-2 be ready to read data! Thread-4 be ready to read data! Thread-0 be ready to read data! Thread-2 have read data :1361 Thread-2 be ready to read data! Thread-4 have read data :1361 我们可以看到 线程1的写是完全互斥的。 而线程2 4 0的读是可以同步进行的。 这是读写锁最简单的例子,下一节,我们看一个稍微复杂的,把读锁与写锁放到一个方法内的例子。 感谢glt
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~