谈谈java并发锁(重入锁、读写锁、公平锁)

目录

重入锁

简单重入锁

重入锁的等待通知(Condition)

多Condition

公平锁和非公平锁

读写锁ReentrantReadWriteLock

锁优化总结:


重入锁和读写锁,他们具有比synchronized更为强大的功能,并且有嗅探锁定、多路分支等功能。

重入锁

在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远进不来的结果。

简单重入锁

//一定要在finally中解锁
//感觉跟synchronized没啥区别,是对象锁。也许性能比较好一些
public class UseReentrantLock {
     
     //private Lock lock = new ReentrantLock();//定义一个锁冲突
     
     public void method1(){
           Lock lock = new ReentrantLock();//定义两个锁不冲突
           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(){
           Lock lock = new ReentrantLock();//定义两个锁不冲突
           try {
                lock.lock();
                System.out.println("当前线程:" +  Thread.currentThread().getName() + "进入method2..");
                Thread.sleep(2000);
                System.out.println("当前线程:" +  Thread.currentThread().getName() + "退出method2..");
                Thread.sleep(1000);
           } catch (InterruptedException e) {
                e.printStackTrace();
           } finally {
                
                lock.unlock();
           }
     }
     
     public static void main(String[] args) {
           final UseReentrantLock ur = new UseReentrantLock();
           final UseReentrantLock ur2 = new UseReentrantLock();
           Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                     ur.method1();
                }
           }, "t1");
           Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                     ur2.method2();
                }
           }, "t2");
           t1.start();
           t2.start();
           try {
                Thread.sleep(10);
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
           //System.out.println(ur.lock.getQueueLength());
     }
}

重入锁的等待通知(Condition)

就像synchronized,wait()、notify()、notifyAll()。

同样,在使用Lock的时候,可以使用一个新的等待/通知的类,它就是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 (Exception 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();        //Object有notifyAll,同理这里也有signalAll()方法
           } catch (Exception e) {
                e.printStackTrace();
           } finally {
                lock.unlock();
           }
     }
     
     public static void main(String[] args) {
           
           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进行多线程间的交互,非常的灵活。可以使得部分需要唤醒的线程唤醒,其他线程则继续等待通知。

//两个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 (Exception 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 (Exception 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 (Exception 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.signal();
           } 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();     // c1
           t2.start();     // c1
           t3.start();     // c2
           
           try {
                Thread.sleep(2000);
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
           t4.start();     // c1
           try {
                Thread.sleep(2000);
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
           t5.start();     // c2
           
     }
}

公平锁和非公平锁

默认是非公平,非公平锁效率比较高。

Lock lock = new ReentrantLock(boolean isFair);//可传参,默认是false非公平锁。

lock用法:

tryLock():尝试获得锁,获得结果用true/false返回。

tryLock():在给定的时间内尝试获得锁,获得结果用true/false返回。

isFair():是否是公平锁。

isLocked():是否锁定。

getHoldCount():查询当前线程保持此锁的个数,也就是调用lock()次数。

lockInterruptibly():优先响应中断的锁。

getQueueLength():返回正在等待获取此锁的线程数。

getWaitQueueLength():返回等待与锁定相关的给定条件Condition的线程数。

hasQueuedThread(Thread thread):查询指定的线程是否在等待此锁。

hasQueuedThreads():查询是否有线程正在等待此锁。

hasWaiters():查询是否有线程正在等待与此锁定有关的condition条件。

读写锁ReentrantReadWriteLock

读写锁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 (Exception 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 (Exception 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();// R
//         t2.start();// R
           
//         t1.start(); // R
//         t3.start(); // W
           
           t3.start();// W
           t4.start();// W
     }
}

锁优化总结:

1.避免死锁。

2.减小锁的持有时间。

3.减小锁的粒度。

4.锁的分离(读写锁)。

5.尽量使用无锁的操作,如原子操作(Atomic系列类),volatile关键字。

你可能感兴趣的:(java,java,多线程)