线程-锁3
1、线程接力
案例:第一个线程打印5次,第二个线程打印10次,第三个线程打印15次,第三个线程执行完毕后,
再从第一个线程继续打印,执行第2轮的操作,总共执行10轮
线程接力代码
在上面的线程接力上用到了java.util.concurrent.locks.Lock、ReentrantLock、Condition
package com.thread.lock.test; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class TestCondition{ Lock lock=new ReentrantLock(); Condition con1=lock.newCondition(); Condition con2=lock.newCondition(); Condition con3=lock.newCondition(); private int flg=1; public void product1(int j){ lock.lock(); try { while(flg!=1){ try { con1.await(); } catch (InterruptedException e) { } } con2.signal(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+"---"+i+"第"+j+"轮"); } flg=2; } finally { lock.unlock(); } } public void product2(int j){ lock.lock(); try { while(flg!=2){ try { con2.await(); } catch (InterruptedException e) { } } con3.signal(); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"---"+i+"第"+j+"轮"); } flg=3; } finally { lock.unlock(); } } public void product3(int j){ lock.lock(); try { while(flg!=3){ try { con3.await(); } catch (InterruptedException e) { } } flg=1; con1.signal(); for (int i = 0; i < 15; i++) { System.out.println(Thread.currentThread().getName()+"---"+i+"第"+j+"轮"); } } finally { lock.unlock(); } } } public class LockConditionTest { public static void main(String[] args) { TestCondition ts=new TestCondition(); new Thread(()->{ for (int i = 0; i < 10; i++) { ts.product1(i+1); } },"第一个线程").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { ts.product2(i+1); } },"第er个线程").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { ts.product3(i+1); } },"第san个线程").start(); } } |
Lock和Synchronized之间的区别:
1、Synchronized是java关键字,给对象加锁,而且这个锁是我们不能控制的(jvm加锁),而lock是juc中提供的接口(他的实现ReentrantLock),由于是用户自己创建出来的锁,所以这个用户是可以控制的。
2、synchronized加锁后,必须获取监听器才可以执行,且他加锁后,是无法中断的,但是lock是可以中断的。
3、Synchronized释放锁是有顺序的,先加的锁后释放,而lock没有这个限制,可以随时的释放锁。
4、lock在使用时,是不可以在使用wait,notify,notifyAll方法,否则会报错的,他使用到了Condition的方法来代替了这几个方法,分别是await、signal、signalAll
2、案例:多个线程访问缓存,如果缓存中没有,读取数据库,如果有,直接获取。
可以参考一下思想来实现:
class CachedData { Object data; volatile boolean cacheValid; final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); try { // Recheck state because another thread might have // acquired write lock and changed state before we did. if (!cacheValid) { data = ... cacheValid = true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); } finally { rwl.writeLock().unlock(); // Unlock write, still hold read } } try { use(data); } finally { rwl.readLock().unlock(); } } }} //ReentrantReadWriteLocks can be used to improve concurrency in some uses of some kinds of Collections. This is typically worthwhile only //when the collections are expected to be large, accessed by more reader threads than writer threads, and entail operations with overhead that //outweighs synchronization overhead. For example, here is a class using a TreeMap that is expected to be large and concurrently accessed. class RWDictionary { private final Map m = new TreeMap(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public Data get(String key) { r.lock(); try { return m.get(key); } finally { r.unlock(); } } public String[] allKeys() { r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); } } public Data put(String key, Data value) { w.lock(); try { return m.put(key, value); } finally { w.unlock(); } } public void clear() { w.lock(); try { m.clear(); } finally { w.unlock(); } } }} |
这里使用ReadWriteLock读写锁来实现这个功能
这个锁也是由我们随意控制的。案例:100个线程读取,1个线程写入
class RedSpider1 { private String data = ""; private ReadWriteLock lock = new ReentrantReadWriteLock(); public void write( String data ) { lock.writeLock().lock(); try { this.data = data; System.out.println( "教师端输入数据 = " + data ); } finally { lock.writeLock().unlock(); } } public String read() { lock.readLock().lock(); try { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "学生端"+Thread.currentThread().getName()+"读取数据 = " + data ); return data; } finally { lock.readLock().unlock(); } } } public class TestJUC_05_rw_lock { public static void main(String[] args) throws Exception { final RedSpider1 rs = new RedSpider1(); // 多个线程的读取不需要加锁。 // 一个线程写数据的时候不能被其他线程读取,应该增加锁。 // 多个线程同时写入数据,也应该加锁。 for ( int i = 0; i < 100; i++ ) { new Thread(new Runnable() { public void run() { rs.read(); } }, ""+i).start(); } Thread.sleep(100); new Thread(new Runnable() { public void run() { rs.write("java juc..."); } }).start(); } } |
synchronized和ReentrantLock的区别
synchronized是关键字,他在加锁后是不能被打断的,因为他是系统加的锁机制(jvm),用户是控制不了。而ReetrantLock是Lock接口的实现类,这个类是可以在加锁后,是可以打断的,是受自己控制的,比较灵活,
而ReetrantLock是不能和wait、notify、notifyAll这些搭配使用的,他一般的用Codition这额条件类的方法的await来替代Object类的wait,用signal来替代notify的,signalAll来替代notifyAll,Lock lock=new ReetrantLock();
Condition c=lock.newCondition();
这个ReetrantLock是一定要自己关闭的,而synchronized是不要我们自己去关闭的,
Synchronized释放锁的顺序是先加的后释放,而ReetrantLock释放锁比较灵活,可以自己定义