文档章节

【Java并发编程实战】– 使用锁实现同步 lock_1

pan_1308
 pan_1308
发布于 2017/09/08 11:24
字数 1690
阅读 8
收藏 0

一、概述

  Lock是 java.util.concurrent.locks 包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题。

------------------------------------------------------------------------------------------------------------

lock 产生的背景:

采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。

因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。另外,通过Lock可以知道线程有没有成功获取到锁。这个是 synchronized 无法办到的。

------------------------------------------------------------------------------------------------------------

sychronized 与 lock 的 比较

1、Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;

2、Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

------------------------------------------------------------------------------------------------------------

需要注意的地方:

sychronized 修饰的方法或者语句块在代码执行完之后锁 自动释放,而用 Lock 需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在 try 内,释放锁放在 finally 内。

------------------------------------------------------------------------------------------------------------

二、实现

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// 打印队列类
public class PrintQueue {

	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	//声明并实例化一个锁对象
	private final Lock queueLock = new ReentrantLock();
	
	//实现 打印方法
	public void printJob(Object doc){
		 queueLock.lock(); // 获取锁对象的控制
		 try {
			System.out.println(Thread.currentThread().getName() + " - printJob 正在打印,请等待...,时间:" + sdf.format(new Date()));
			TimeUnit.SECONDS.sleep(2);
		 } catch (InterruptedException e) {
			e.printStackTrace();
		 } finally{
			queueLock.unlock(); // 释放锁对象的控制.(一般是 finally 中手动释放)
		 }
	}
	
}
import java.text.SimpleDateFormat;
import java.util.Date;

public class Job implements Runnable{

	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	
	private PrintQueue printQueue;
	public Job(PrintQueue printQueue){
		this.printQueue = printQueue;
	}
	
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "准备打印...,时间:" + sdf.format(new Date()));
		printQueue.printJob(new Object()); // 发送 打印任务.
		System.out.println(Thread.currentThread().getName() + "完成打印...,时间:" + sdf.format(new Date()));
	}

}
public class TestJob {

	public static void main(String[] args) {
		PrintQueue printQueue = new PrintQueue();
		for(int i=0;i<3;i++){
			Thread thread = new Thread(new Job(printQueue)); //3个线程创建3个打印工作
			thread.start();
		}
	}
	
	/** console 结果:
	 *  Thread-1准备打印...,时间:2017-09-08 10:43:06
		Thread-2准备打印...,时间:2017-09-08 10:43:06
		Thread-0准备打印...,时间:2017-09-08 10:43:06
		Thread-1 - printJob 正在打印,请等待...,时间:2017-09-08 10:43:06
		Thread-1完成打印...,时间:2017-09-08 10:43:08
		Thread-2 - printJob 正在打印,请等待...,时间:2017-09-08 10:43:08
		Thread-2完成打印...,时间:2017-09-08 10:43:10
		Thread-0 - printJob 正在打印,请等待...,时间:2017-09-08 10:43:10
		Thread-0完成打印...,时间:2017-09-08 10:43:12
	 */
}

分析

示例 主要部分是打印队列类 PrintQueue中的 printJob()方法。

使用 锁实现了一个临界区,并且保证同一个时间只有一个执行线程访问这个临界区时,必须创建 ReentrantLock 对象。在这个临界区的开始,必须通过 lock() 方法 获取对锁的控制。当线程A 访问这个方法时,如果没有其他线程获取对这个锁的控制,lock() 方法将让线程 A 获得 锁并且允许 它立即执行 临界区代码。否则,如果其他线程 B 正在执行这个锁保护的临界区代码,lock() 方法将让线程 A 休眠直到 线程 B 执行完临界区的代码。

在线程 离开临界区的时候,必须使用 unlock()方法来 释放它持有的锁,以让其他线程来访问临界区。如果在离开临界区的时候没有调用 unlock() 方法,其他线程将永久地等待,从而导致死锁情景。如果在 临界区使用了 try-catch 块,记得必须将 unlock() 方法 放入 finally部分。

三、常用方法

 1、lock()  

      如果锁已被其他线程获取,则进行等待。如果采用 Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此,一般来说,使用Lock必须在 try…catch… 块中进行,并且将释放锁的操作放在 finally 块中进行,以保证锁一定被被释放,防止死锁的发生。

Lock lock = ...;
lock.lock();
try{
    //处理任务
}catch(Exception ex){

}finally{
    lock.unlock();   //释放锁
}

2、tryLock() & tryLock(long time, TimeUnit unit)

tryLock() 方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true;如果获取失败(即锁已被其他线程获取),则返回false,也就是说,这个方法无论如何都会立即返回(在拿不到锁时不会一直在那等待)。

tryLock (long time, TimeUnit unit) 方法和 tryLock() 方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false,同时可以响应中断。如果一开始拿到锁或者在等待期间内拿到了锁,则返回 true。

Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){

     }finally{
         lock.unlock();   //释放锁
     } 
}else {
    //如果不能获取锁,则直接做其他事情
}

四、Lock和synchronized的选择

总的来说,Lock和synchronized有以下几点不同:

1、 Lock是一个接口,是JDK层面的实现;而synchronized是Java中的关键字,是Java的内置特性,是JVM层面的实现;

2、 synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3、Lock 可以让等待锁的线程响应中断,而使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4、 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;

5、 Lock可以提高多个线程进行 读操作 的效率。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的。而当竞争资源非常激烈时(即有大量线程同时竞争),此时 Lock 的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

 

© 著作权归作者所有

共有 人打赏支持
pan_1308
粉丝 4
博文 95
码字总数 58819
作品 0
黄冈
【死磕Java并发】—– 死磕 Java 并发精品合集

【死磕 Java 并发】系列是 LZ 在 2017 年写的第一个死磕系列,一直没有做一个合集,这篇博客则是将整个系列做一个概览。 先来一个总览图: 【高清图,请关注“Java技术驿站”公众号,回复:脑...

chenssy
07/22
0
0
Java 使用 happen-before 规则实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。按照官方的...

stateIs0
01/20
0
0
读书笔记之《Java并发编程的艺术》-java中的锁

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
0
Java中高级面试必问之多线程TOP50(含答案)

以下为大家整理了今年一线大厂面试被问频率较高的多线程面试题,由于本人的见识局限性,所以可能不是很全面,也欢迎大家在后面留言补充,谢谢。 1、什么是线程? 2、什么是线程安全和线程不安...

老道士
08/28
0
0
Java 8新特性探究(十)StampedLock将是解决同步问题的新宠

Java8就像一个宝藏,一个小的API改进,也足与写一篇文章,比如同步,一直是多线程并发编程的一个老话题,相信没有人喜欢同步的代码,这会降低应用的吞吐量等性能指标,最坏的时候会挂起死机,...

OSC闲人
2014/05/13
0
30

没有更多内容

加载失败,请刷新页面

加载更多

VSCode 搭建Vue开发环境之Vue CLI

一、简介说明 1.关于VS Code开发工具,安装和配置,更多可以参考以前文章 2.关于Vue.js,Vue是一个优秀的渐进式前端框架,不仅易于上手,还便于与第三方库或既有项目整合。 3.关于Vue是使用方...

tianma3798
24分钟前
2
0
MySQL 相关博客整理

1. 《深入理解 MySQL 底层实现》 简评:文章从硬盘底层存储原理讲解到MySQL存储原理,其中涉及InnoDB 和 Myisam 中 B+Tree 的应用,以及常见数据库优化思路,算是一片很不错的讲解MySQL原理的...

科陆李明
34分钟前
2
0
pada rabbitmq server mangage

查看配置文件 ubuntu@node4:/etc/rabbitmq$ lltotal 28drwxr-xr-x 2 rabbitmq rabbitmq 4096 Jun 6 13:52 ./drwxr-xr-x 104 root root 12288 Sep 26 11:39 ../-rw-r--r-- ......

qwfys
41分钟前
0
0
SpringBoot进阶

慕课网链接 表单数据的验证 在pojo类属性的上面添加注解 @Entitypublic class Girl { @Id @GeneratedValue private Integer id; @NotBlank(message = "这个字段...

踏破铁鞋无觅处
48分钟前
1
0
【SylixOS】QT-QWS流程介绍

QWS简介 QWS(QT Windows System)是QT自行开发的窗口系统,体系结构类似X Windows的C/S结构。QWS Server在物理设备上显示,QWS Client实现界面,两者通过socket进行彼此的通讯。在很多嵌入式系...

suokin
48分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部