锁
在java多线程中,我们可以使用Synchronized关键字来实现线程间的同步互斥工作,那么其中有一个更优秀的机去完成这个”同步互斥”工作,它就是lock对象,我们主要学习两种锁重入锁和读写锁,他们具有比Synchronized更为强大的功能,并且有嗅探锁定、多路分支等功能。
ReentrantLock重入锁
在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远进不来的情况。
public class UseReentrantLock {
private ReentrantLock lock = new ReentrantLock();
public void method1(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入method1...");
Thread.sleep(1000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出method1...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public void method2(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入method2..");
Thread.sleep(2000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出method2...");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public static void main(String[] args) {
final UseReentrantLock ur = new UseReentrantLock();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
ur.method1();
ur.method2();
}
},"t1");
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
锁与等待通知
在使用Synchronized的时候,如果需要多线程键进行协作工作则需要Object的wait()和notify()/notifyAll()方法配合工作。
那么同样,我们在使用Lock的时候,可以使用一个新的等待/通知的类,Condition。这个Condition一定是针对具体的某一把锁的。也就是只有锁的基础上才会产生Condition。
使用Condition类进行通信
public class UseCondition {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void method1(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入等待状态..");
Thread.sleep(3000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"释放锁..");
condition.await();//object wait 会释放锁
System.out.println("当前线程:"+Thread.currentThread().getName()+"继续执行..");
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public void method2(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入..");
Thread.sleep(3000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"发出唤醒..");
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public static void main(String[] args) {
CopyOnWriteArrayList a = new CopyOnWriteArrayList();
final UseCondition uc = new UseCondition();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
uc.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
uc.method2();
}
},"t2");
t1.start();
t2.start();
}
}
多Condition
我们可以通过lock对象产生多个Condition进行线程间交互。
public class UseManyCondition {
private ReentrantLock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
public void m1(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入m1方法等待..");
c1.await();
System.out.println("当前线程:"+Thread.currentThread().getName()+"方法m1继续..");
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public void m2(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入m2方法等待..");
c1.await();
System.out.println("当前线程:"+Thread.currentThread().getName()+"方法m2继续..");
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public void m3(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入m3方法等待..");
c2.await();
System.out.println("当前线程:"+Thread.currentThread().getName()+"方法m3继续..");
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public void m4(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"唤醒...");
c1.signalAll();
} catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void m5(){
try {
lock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"唤醒...");
c2.signalAll();
} catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
final UseManyCondition umc = new UseManyCondition();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
umc.m1();
}
},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
umc.m2();
}
},"t2");
Thread t3 = new Thread(new Runnable(){
@Override
public void run() {
umc.m3();
}
},"t3");
Thread t4 = new Thread(new Runnable(){
@Override
public void run() {
umc.m4();
}
},"t4");
Thread t5 = new Thread(new Runnable(){
@Override
public void run() {
umc.m5();
}
},"t5");
t1.start();
t2.start();
t3.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t4.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t5.start();
}
}
Lock/Condition其他方法和用法
公平锁和非公平锁
Lock lock = new ReentranLock(boolean isNonFair);
公平锁维护顺序,非公平锁没有顺序。
Lock用法:
tryLock():尝试获得锁,获得结果用true/false返回。
isFair()
isLocked()
getHoldCount()
lockInterruptibly()
getQueueLength()
getWaitQueueLength()
hasQueuedThread(Thread thread)
hasQueuedThreads()
hasWaiters()
读写锁ReentrantReadWriteLock
其核心就是实现读写分离的锁。在高并发访问下,尤其是读多写少的情况下,效率明显高于重入锁。之前学Synchronized、ReentrantLock时,我们知道同一时间内,只能有一个线程进行访问被锁定的代码,那么读写锁就不同,其本质就是分成两个锁,即读锁写锁。在读锁下多个线程可以并发的访问,但在写锁的情况下只能一个一个的顺序访问。
口诀:读读共享 写写互斥 读写互斥
public class UseReentrantReadWriteLock {
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
//获取读写锁
private ReadLock readLock = rwLock.readLock();
private WriteLock writeLock = rwLock.writeLock();
public void read(){
try {
readLock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入...");
Thread.sleep(3000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
readLock.unlock();
}
}
public void write(){
try {
writeLock.lock();
System.out.println("当前线程:"+Thread.currentThread().getName()+"进入...");
Thread.sleep(3000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"退出...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
writeLock.unlock();
}
}
public static void main(String[] args) {
final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
urrw.read();
}
},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
urrw.read();
}
},"t2");
Thread t3 = new Thread(new Runnable(){
@Override
public void run() {
urrw.write();
}
},"t3");
Thread t4 = new Thread(new Runnable(){
@Override
public void run() {
urrw.write();
}
},"t4");
//读读共享
t1.start();
t2.start();
//写写互斥
t3.start();
t4.start();
//读写互斥
t1.start();
t3.start();
}
}