Lock与Condition结合,等待通知机制await(),signal()与singanlAll()
程序员文章站
2022-05-05 09:57:25
...
java.util.concurrent并发包下
一.Lock显式锁性能比synchronized高很多。
1.尝试非阻塞的获取锁
2.获取锁的过程中可以被中断
3.超时可以放弃获取锁
而线程在尝试获取synchronized(阻塞锁)的过程中不允许被中断。
二.Lock和synchronized锁的共同点:可重入,非公平。
1.可重入(默认):在递归时,会发生锁的重入现象。线程可以再次获取自己内部的锁。比如有线程A获得了某锁,此时锁还没有释放,当线程A再次想要获取这个锁的时候还可获取,如果锁不可重入,则会发生死锁。
2.公平锁:先对锁发出获取请求的线程一定会先被满足。非公平锁的性能(默认)>公平锁,因为在线程等待挂起到恢复的过程中会存在一定的延时。
ReentrantLock无参构造:默认使用不公平锁
三.读写锁ReentrantReadWriteLock。分为读锁和写锁
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable
private final ReentrantReadWriteLock.ReadLock readerLock;
private final ReentrantReadWriteLock.WriteLock writerLock;
同一时刻下,允许多个读线程同时进行,只允许一个写线程,适用场景:读多写少。性能远超synchronized。
三.使用Lock与Condition结合(await,signal,signalAll)代替Object的(wait,notify,notifyAll)。
siganl()会唤醒其condition中的一个等待线程,那么问题是如果有多个线程(同一个conditon)在等待,被唤醒的是谁呢?
public class ConditionTemplate {
Lock lockLC=null;
private Condition c1=null;
private Condition c2=null;
private int i;
public ConditionTemplate(ReentrantLock lock,int i){
this.i=i;
lockLC=lock;
c1=lockLC.newCondition();
c2=lockLC.newCondition();
}
public void waitC1() throws InterruptedException{
lockLC.lock();
try{
while(i == 0){
System.out.println("c1锁");
c1.await();
}
System.out.println("解锁c1");
}finally {
lockLC.unlock();
}
}
public void waitC2() throws InterruptedException{
System.out.println("c2锁");
lockLC.lock();
try{
while(i == 0){
c2.await();
}
System.out.println("解锁c2");
}finally {
lockLC.unlock();
}
}
public void release(int j) throws InterruptedException{
lockLC.lock();
try{
System.out.println("释放");
this.i=j;
c2.signal();//c2释放
}finally {
lockLC.unlock();
}
}
}
public class mainTest {
private static class t1 extends Thread{
private ConditionTemplate conditionTemplate;
public t1(ConditionTemplate ConditionTemplate){
this.conditionTemplate=ConditionTemplate;
}
@Override
public void run() {
try {
System.out.println("run|t1:"+Thread.currentThread().getId());
conditionTemplate.waitC1();
System.out.println("释放后:"+Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static class t2 extends Thread{
private ConditionTemplate conditionTemplate;
public t2(ConditionTemplate ConditionTemplate){
this.conditionTemplate=ConditionTemplate;
}
@Override
public void run() {
try {
System.out.println("run|t2:"+Thread.currentThread().getId()+"|"+new Date().getTime());
conditionTemplate.waitC2();
System.out.println("释放后:"+Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException{
final ConditionTemplate conditionTemplate=new ConditionTemplate(new ReentrantLock(),0);
Thread t1=new t1(conditionTemplate);
Thread t2=new t2(conditionTemplate);
Thread t4=new t2(conditionTemplate);
Thread t3=new Thread(new Runnable() {
@Override
public void run() {
try {
conditionTemplate.release(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread.sleep(500);
t2.start();
Thread.sleep(500);
t4.start();
Thread.sleep(500);
t3.start();
}
}
运行后的结果:
多次试验后,signal()唤醒的是(在同一条件下)等待时间最长的线程。
至于为什么是等待时间最长的线程?ReentrantLock中AQS(抽象队列同步器)原理 (后续整理后更新)