线程-锁3

1、线程接力
案例:第一个线程打印5次,第二个线程打印10次,第三个线程打印15次,第三个线程执行完毕后,
再从第一个线程继续打印,执行第2轮的操作,总共执行10轮
线程接力代码
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();
}


}
在上面的线程接力上用到了java.util.concurrent.locks.Lock、ReentrantLock、Condition
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释放锁比较灵活,可以自己定义
 


猜你喜欢

转载自blog.csdn.net/wojiao228925661/article/details/80696549