Java多线程-Lock
ReentrantLock
ReentrantLock可以和synchronized达到一样的效果,并且扩展功能上也更加强大,而且使用更加灵活。
Lock lock=new ReentrantLock();
//加锁
lock.lock();
//取消锁
lock.unlock();
等待通知模式:
synchronized与wait()和notify方法结合可以实现等待通知模式
wait():释放占有的对象锁,线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。而sleep()不同的是,线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁。也就是说,在休眠期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得cpu,执行代码。wait()和sleep()最大的不同在于wait()会释放对象锁,而sleep()不会!
notify(): 该方法会唤醒因为调用对象的wait()而等待的线程,其实就是对对象锁的唤醒,从而使得wait()的线程可以有机会获取对象锁。调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。JVM则会在等待的线程中调度一个线程去获得对象锁,执行代码
1个线程访问A的代码,拿到obj的锁,但是执行了obj.wait()马上就释放了obj的锁
这时候另一个拿到obj锁进入B的代码,但是执行了obj.notify(),通知等待的线程准备启动
//A
synchronized (obj){
obj.wait();
}
//B
synchronized (obj){
obj.notify();
}
ReentrantLock可可以实现同样的功能。而且更加灵活,可以实现选择性通知。
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
try {
lock.lock();
System.out.println("A");
//等待
condition.await();
System.out.println("B");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
// 释放
// condition.signal();
执行结果为:显示A,然后线程处于WAITING状态
注意: condition.await();必须在lock()范围内,不然会报异常。
Object类的wait(),相当于Condition的await();
Object类的wait(long timeout),相当于Condition的await(long time,TimeUnit unit);
Object类的notify(),相当于Condition的signal();
Object类的notifyAll(),相当于Condition的signalAll();
多个Condition
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
conditionA 只能唤醒conditionA 的await(),也就是每个Condition 都是单独隔离的
公平锁,非公平锁:
公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的(lock.lock()顺序),即先来的先得到。
非公平锁是一个锁的抢占机制,是随机获取锁,可能造成某些线程一直拿不到锁。
//公平锁
Lock lock=new ReentrantLock(true);
//非公平锁
Lock lock=new ReentrantLock(false);
//默认非公平锁
Lock lock=new ReentrantLock();
方法
//查询当前线程保持锁定的个数,就是调用lock()方法次数
lock.getHoldCount();
//返回正等待获取此锁定的线程估计数
lock.getQueueLength();
//返回等待与此锁定相关的给定条件Condition的线程估计数
lock.getWaitQueueLength(condition);
//查询指定的线程是否正在等待此锁
lock.hasQueuedThread(thread);
//查询是否有线程正在等等此锁
lock.hasQueuedThreads();
//查询是否有线程正在等待此锁定有关的condition条件
lock.hasWaiters(condition);
//是否是公平锁
lock.isFair();
//查询当前线程是否保持锁
lock.isHeldByCurrentThread();
//查询此锁是否由任意的线程保持
lock.isLocked();
//如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常
lock.lockInterruptibly();
//仅在调用时锁未被其他线程锁定的情况下,才获取该锁。
lock.tryLock();
//锁定在给等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
lock.tryLock(long timeout,TimeUnit unit);
ReentrantReadWriteLock
ReentrantLock具有完全互斥的效果,同一个时间只有一个线程在执行lock()方法后面的内容,保证了实例变量的线程安全性。但是效率比较低下。
ReentrantReadWriteLock读写锁,有两个锁。一个是读操作相关的锁,也叫做共享锁;另一个是写操作的相关的锁,也叫做排他锁。(1)也就是多个读锁间不互斥;(2)读锁写锁互斥;(3)写锁写锁互斥。
在没有线程Thread进入写入操作时,进行读取操作的多个Thread都可以读取读锁,而进入写入操作的Thread只有在获取写锁后才能进行写入操作。即多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作
ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
lock.readLock().lock();
lock.writeLock().lock();
过程:
“读写”、“写读”、“写写”,互斥
当一个线程已经获得了读锁,而且还没释放正在运行,这时候有一个线程要获取写锁,则等待。
当一个线程已经获得了写锁,而且还没释放正在运行,这时候有一个线程要获取读锁,则等待。
当一个线程已经获得了写锁,而且还没释放正在运行,这时候有一个线程要获取写锁,则等待。
“读读”,不互斥
当多个线程都想要获得一个读锁时,发现没有其他线程拥有写锁,则运行