JAVA并发包——锁

1.java多线程中,可以使用synchronized关键字来实现线程间的同步互斥工作,其实还有个更优秀的机制来完成这个同步互斥的工作——Lock对象,主要有2种锁:重入锁和读写锁,它们比synchronized具有更强大的功能,并且有嗅探锁定、多路分支等功能。

2.ReentrantLock(重入锁)

重入锁,在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,否则会造成锁永远无法释放,其他线程永远进不来的结果(使用起来跟synchronized很像,并且,在jdk1.8之前,ReentrantLock比synchronized性能好,jdk1.8对synchronized做了优化,性能接近了。但是ReentrantLock比synchronized灵活)

代码示例:

 1 package lock020;
 2 
 3 import java.util.concurrent.locks.Lock;
 4 import java.util.concurrent.locks.ReentrantLock;
 5 
 6 public class UseReentrantLock {
 7     
 8     private Lock lock = new ReentrantLock();
 9     
10     public void method1(){
11         try {
12             lock.lock();
13             System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");
14             Thread.sleep(1000);
15             System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");
16             Thread.sleep(1000);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         } finally {
20             
21             lock.unlock();
22         }
23     }
24 
25     public static void main(String[] args) {
26 
27         final UseReentrantLock ur = new UseReentrantLock();
28         Thread t1 = new Thread(new Runnable() {
29             @Override
30             public void run() {
31                 ur.method1();
32             }
33         }, "t1");
34 
35         t1.start();
36 
37         Thread t2 = new Thread(new Runnable() {
38             @Override
39             public void run() {
40                 ur.method1();
41             }
42         }, "t2");
43 
44         t2.start();
45 
46         try {
47             Thread.sleep(10);
48         } catch (InterruptedException e) {
49             e.printStackTrace();
50         }
51         //System.out.println(ur.lock.getQueueLength());
52     }
53     
54     
55 }

 

执行以后,可以发现,t1和t2是串行执行method1的

3.ReentrantLock锁的等待与通知

synchronized关键字里,有Object的wait()方法和notify()/notifyAll()方法进行多线程之间的工作协调。而同样的,Lock也有自己的等待/通知类,它就是Condition。这个Condition一定是针对某一把具体的锁的,就是说,只有有锁的基础之才会产生Condition

代码实现:

 1 package lock020;
 2 
 3 import java.util.concurrent.locks.Condition;
 4 import java.util.concurrent.locks.Lock;
 5 import java.util.concurrent.locks.ReentrantLock;
 6 
 7 public class UseCondition {
 8 
 9     private Lock lock = new ReentrantLock();
10     private Condition condition = lock.newCondition();
11     
12     public void method1(){
13         try {
14             lock.lock();
15             System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");
16             Thread.sleep(3000);
17             System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");
18             condition.await();    // Object wait,释放锁
19             System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");
20         } catch (Exception e) {
21             e.printStackTrace();
22         } finally {
23             lock.unlock();
24         }
25     }
26     
27     public void method2(){
28         try {
29             lock.lock();
30             System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
31             Thread.sleep(3000);
32             System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
33             condition.signal();        //Object notify
34         } catch (Exception e) {
35             e.printStackTrace();
36         } finally {
37             lock.unlock();
38         }
39     }
40     
41     public static void main(String[] args) {
42         
43         final UseCondition uc = new UseCondition();
44         Thread t1 = new Thread(new Runnable() {
45             @Override
46             public void run() {
47                 uc.method1();
48             }
49         }, "t1");
50         Thread t2 = new Thread(new Runnable() {
51             @Override
52             public void run() {
53                 uc.method2();
54             }
55         }, "t2");
56 
57         t1.start();
58         t2.start();
59     }
60     
61     
62     
63 }

 

以上代码,实现了wait/nofity的功能

ps:不同的线程之间,可以用不同的Condition对象来进行通信。例如t1唤醒t2用 Condition1,t3唤醒t4用Condition2,这也是比wait/notify灵活的地方

3.公平锁和非公平锁

公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的;而非公平锁是允许插队的。(默认是非公平锁)

可以通过构造方法指定参数是ture(公平)或者false(非公平),一般说来,非公平锁比公平锁性能要好,因为公平锁要维护顺序

4.ReentrantLock锁与synchronized的区别

类别synchronizedLock
存在层次 Java的关键字,在jvm层面上 是一个类
锁的释放 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 在finally中必须释放锁,不然容易造成线程死锁
锁的获取 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
锁状态 无法判断 可以判断
锁类型 可重入 不可中断 非公平 可重入 可判断 可公平(两者皆可)
性能 少量同步 大量同步

5.ReenTrantReadWriteLock(读写锁)

核心其实就是实现读写分离。在高并发的情况下,尤其是读多写少的情况下,性能远高于重入锁

之前的ReentrantLock和synchronized的使用时,同一时间内,只能一个线程访问被锁定的代码。读写锁不同,其本质是2个锁,即读锁和写锁。在读锁下,多个线程可以并发访问,但是在写锁下,只能串行访问

口诀:读读共享,写写互斥,读写互斥

代码示例:

 1 package lock021;
 2 
 3 import java.util.concurrent.locks.ReentrantReadWriteLock;
 4 import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
 5 import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
 6 
 7 public class UseReentrantReadWriteLock {
 8 
 9     private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
10     private ReadLock readLock = rwLock.readLock();
11     private WriteLock writeLock = rwLock.writeLock();
12     
13     public void read(){
14         try {
15             readLock.lock();
16             System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
17             Thread.sleep(3000);
18             System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
19         } catch (Exception e) {
20             e.printStackTrace();
21         } finally {
22             readLock.unlock();
23         }
24     }
25     
26     public void write(){
27         try {
28             writeLock.lock();
29             System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
30             Thread.sleep(3000);
31             System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
32         } catch (Exception e) {
33             e.printStackTrace();
34         } finally {
35             writeLock.unlock();
36         }
37     }
38     
39     public static void main(String[] args) {
40         
41         final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();
42         
43         Thread tr1 = new Thread(new Runnable() {
44             @Override
45             public void run() {
46                 urrw.read();
47             }
48         }, "t1");
49         Thread tr2 = new Thread(new Runnable() {
50             @Override
51             public void run() {
52                 urrw.read();
53             }
54         }, "t2");
55         Thread tw3 = new Thread(new Runnable() {
56             @Override
57             public void run() {
58                 urrw.write();
59             }
60         }, "t3");
61         Thread tw4 = new Thread(new Runnable() {
62             @Override
63             public void run() {
64                 urrw.write();
65             }
66         }, "t4");        
67         
68         tr1.start();
69         tr2.start();
70 
71         tw3.start();
72         tw4.start();
73 
74     }
75 }

 

模拟代码可发现:

如果是都是读操作,基本是同时进行的

如果有写操作,是要锁定的

6.锁的优化

(1)避免死锁

(2)减小锁的持有时间

(3)减小锁的粒度

(4)锁的分离

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

7.分布式锁的概念

2台机器都部署了项目,显然,运行的jvm是不同的,如果t1线程在机器A,t2线程在机器B,同时要访问同一段锁定的代码块

显然,jvm的锁机制是无法处理这种情况的,这时候要考虑第三方帮助实现,如zookkeeper

 

posted @ 2018-05-20 16:50  无名草110  阅读(309)  评论(0编辑  收藏  举报