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的区别

类别 synchronized Lock
存在层次 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

JAVA并发包——锁的更多相关文章

  1. 深入浅出Java并发包—锁机制(三)

    接上文<深入浅出Java并发包—锁机制(二)>  由锁衍生的下一个对象是条件变量,这个对象的存在很大程度上是为了解决Object.wait/notify/notifyAll难以使用的问题. ...

  2. 深入浅出Java并发包—锁机制(二)

    接上文<深入浅出Java并发包—锁机制(一)  >  2.Sync.FairSync.TryAcquire(公平锁) 我们直接来看代码 protected final boolean tr ...

  3. 深入浅出Java并发包—锁机制(一)

    前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Jav ...

  4. 深入浅出Java并发包—锁(Lock)VS同步(synchronized)

    今天我们来探讨一下Java中的锁机制.前面我们提到,在JDK1.5之前只能通过synchronized关键字来实现同步,这个前面我们已经提到是属于独占锁,性能并不高,因此JDK1.5之后开始借助JNI ...

  5. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  6. Java并发包——线程同步和锁

    Java并发包——线程同步和锁 摘要:本文主要学习了Java并发包里有关线程同步的类和锁的一些相关概念. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520 ...

  7. Java并发包下锁学习第一篇:介绍及学习安排

    Java并发包下锁学习第一篇:介绍及学习安排 在Java并发编程中,实现锁的方式有两种,分别是:可以使用同步锁(synchronized关键字的锁),还有lock接口下的锁.从今天起,凯哥将带领大家一 ...

  8. Java并发包下锁学习第二篇Java并发基础框架-队列同步器介绍

    Java并发包下锁学习第二篇队列同步器 还记得在第一篇文章中,讲到的locks包下的类结果图吗?如下图: ​ 从图中,我们可以看到AbstractQueuedSynchronizer这个类很重要(在本 ...

  9. Java并发包源码学习系列:ReentrantLock可重入独占锁详解

    目录 基本用法介绍 继承体系 构造方法 state状态表示 获取锁 void lock()方法 NonfairSync FairSync 公平与非公平策略的差异 void lockInterrupti ...

随机推荐

  1. day1(初始化项目结构)

    1.初始化项目结构  └─shiyanlou_project    │  .gitignore    │  README.en.md           # 英文    │  README.md    ...

  2. moviepy音视频开发:音频合成类CompositeAudioClip介绍

    ☞ ░ 前往老猿Python博文目录 ░ CompositeAudioClip是AudioClip的直接子类,用于将几个音频剪辑合成为一个音频剪辑.CompositeAudioClip类只有一个构造方 ...

  3. Python正则表达式re.match(r"(..)+", "a1b2c3")匹配结果为什么是”c3”?

    在才开始学习正则表达式处理时,老猿对正则表达式:re.match(r"(-)+", "a1b2c3") 返回的匹配结果为"c3"没有理解,学 ...

  4. PyQt(Python+Qt)学习随笔:QListWidget获取指定行对应项的item()方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 在列表部件中,可以通过item方法获取指定行对应的项,语法如下: QListWidgetItem i ...

  5. HTTP助记

    1** 信息,服务器收到请求,需要请求者继续执行操作 100 continue 继续,客户端应继续请求 101 swithching protocls 切换协议,服务器根据客户端的请求切换协议.只能切 ...

  6. 项目使用RQ队列的思考

    碎遮项目的后端异步处理经历了 无处理->多线程/多进程->celery异步队列->RQ队列 的调整和修改,先简单说明一下为什么会存在这样的过程. 在nmap的使用指南中,提到过这样的 ...

  7. 图论——迪杰斯特拉算法(Dijkstra)实现,leetcode

    迪杰斯特拉算法(Dijkstra):求一点到另外一点的最短距离 两种实现方法: 邻接矩阵,时间复杂度O(n^2) 邻接表+优先队列,时间复杂度O(mlogn)(适用于稀疏图) (n:图的节点数,m:图 ...

  8. 通过CSS绘制五星红旗

    任务要求: 1.创建一个div作为红旗旗面,用CSS控制其比例宽高比为3:2,背景为红色. 2.再创建五个小的div,用CSS控制其大小和位置. 3.用CSS同时控制每个小div的大小.边框和位置,同 ...

  9. JavaScript:常用的一些数组遍历的方法

    常用的一些遍历数组的方法: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  10. Docker的容器使用与连接-Window

    启动容器 启动容器之前需要先拉取镜像,然后通过 run 命令启动容器,同一个镜像可以启动多个容器,只要执行多次 run 命令就行了.我们这边启动 centos 的镜像. PS D:\> dock ...