Java并发基础--Lock的学习
一、Lock的出现
Lock的主要作用实现线程之间的同步互斥,与synchronized关键字的效果是一样的,synchronized是Java语言内置的特性,那么为什么又出现了Lock呢?原因是synchronized不是完美的,那么synchronized的缺陷在哪里呢?
①、通过synchronized实现同步,如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
②、通过synchronized实现同步的时候,无法知道线程是否获取到了锁。
为了弥补synchronized的缺陷,Java 5中引入了新的锁机制——java.util.concurrent.locks中的显式的互斥锁。Lock接口,提供了比synchronized更加广泛的锁操作。它的主要实现类:ReentrantLock、ReetrantReadWriteLock.ReadLock和ReetrantRead.WriteLock.即重入锁、读锁和写锁。Lock必须被显式的创建和释放,为了保证最终锁一定被释放,经常将虎互斥区置放在try语句中,并在finally中释放锁,特别是当有return语句的时候,return语句必须放在try语句中,以确保unlock不会过早的发生,从而将数据暴露给下面的任务。使用比较多的是ReentrantLock。示例如下:
public class LockDemo {
private Lock lock = new ReentrantLock();
public void testMethod(){
try {
lock.lock();
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+" print....");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
LockDemo lockDemo = new LockDemo();
new Thread(new MyThread1(lockDemo)).start();
new Thread(new MyThread1(lockDemo)).start();
}
}
class MyThread1 implements Runnable{
private LockDemo lockDemo;
public MyThread1(LockDemo lockDemo){
this.lockDemo = lockDemo;
}
@Override
public void run() {
lockDemo.testMethod();
}
}
结果输出:
Thread-0 print....
Thread-0 print....
Thread-0 print....
Thread-1 print....
Thread-1 print....
Thread-1 print....
总结:
- Lock是一个接口,它有不同的实现类,通过它的实现类可以实现多线程间的同步互斥。
- Lock与synchronized最大的不同在于,synchronized不需要用户手动释放锁,在线程完成了同步块或者同步方法后自动释放锁(或者出现异常也自动释放锁)。而Lock锁需要用户手动释放,如果忘记释锁,可能造成死锁等后果。
二、Lock的详细学习

通过源码可以知道,Lock是一个接口,lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。newCondition()主要是用来创建Condition对象的,线程对象可以注册在指定的Condition上,Condition起到一个对象监视器的作用,通过Condition实例从而进行线程通知,在调用线程上更加灵活。
1.lock()方法
lock方法是使用最多的一个方法,即用来获取锁的,如果锁已经被其他线程占用,则进行等待。经常需要结合try语句一起使用。目的是为了将锁的释放工作放在finally中进行,保证锁的最终释放。如下:
Lock lock = new ReentrantLock(); lock.lock();
try{
//处理任务
}catch(Exception ex){ }finally{
lock.unlock(); //释放锁
}
2.tryLock()方法
tryLock()是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
3.tryLock(long time, TimeUnit unit)方法
tryLock(long time, TimeUnit unit)方法类似tryLock(),区别在于这个方法在拿不锁的时候,不会立即返回,而是等待指定的时间,如果超过指定时间还未获取到锁,则立即返回false,获取锁失败。
Lock lock = new ReentrantLock();
if(lock.tryLock(3, TimeUnit.SECONDS)) {
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}
4.lockInterruptibly()方法
lockInterruptibly()方法,比较特使,可以说是Lock锁的特点之一,即线程在获取锁的过程中,如果一直获取不到锁,它不会立即放弃,直到这个线程被中断。即它能够响应中断, void lockInterruptibly() throws InterruptedException;接口的方法定义是声明了异常的,lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。它的使用形式如下:
public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}
5.newCondition()方法
在使用notify()/notifyAll()方法的时候,被唤醒的线程是JVM随机选择的,最终能够获取锁的线程也许不是我们想要的。在Lock中结合Condition类可以实现“选择性通知”。即可以通过Condition对象唤醒指定的线程。示例如下:
public class LockConditionDemo {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void testMethod(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" test begin");
condition.await();//当前线程进入等待状态,释放lock锁
System.out.println(Thread.currentThread().getName()+" test end");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
System.out.println(Thread.currentThread().getName()+" 释放锁");
}
}
public void testMethod2(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" test2 begin");
condition.signal();//通知处于等待lock锁状态的线程
System.out.println(Thread.currentThread().getName()+" test2 end");
}finally{
lock.unlock();
System.out.println(Thread.currentThread().getName()+" 释放锁");
}
}
public static void main(String[] args) {
LockConditionDemo lockConditionDemo = new LockConditionDemo();
new Thread(new Mythread3(lockConditionDemo)).start();
new Thread(new Mythread4(lockConditionDemo)).start();
}
}
class Mythread3 implements Runnable{
private LockConditionDemo lockConditionDemo;
Mythread3(LockConditionDemo lockConditionDemo){
this.lockConditionDemo = lockConditionDemo;
}
@Override
public void run() {
lockConditionDemo.testMethod();
}
}
class Mythread4 implements Runnable{
private LockConditionDemo lockConditionDemo;
Mythread4(LockConditionDemo lockConditionDemo){
this.lockConditionDemo = lockConditionDemo;
}
@Override
public void run() {
lockConditionDemo.testMethod2();
}
}
结果输出:
Thread-0 test begin
Thread-1 test2 begin
Thread-1 test2 end
Thread-1 释放锁
Thread-0 test end
Thread-0 释放锁
三、Lock和synchronized的选择
1.lock和synchronized的不同
Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现。
synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。
Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断
通过Lock可以知道有没有成功获取锁,而synchronized却无法办到
Lock可以提高多个线程进行读操作的效率
Java并发基础--Lock的学习的更多相关文章
- java并发基础及原理
java并发基础知识导图 一 java线程用法 1.1 线程使用方式 1.1.1 继承Thread类 继承Thread类的方式,无返回值,且由于java不支持多继承,继承Thread类后,无法再继 ...
- Java 并发基础
Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...
- Java并发基础概念
Java并发基础概念 线程和进程 线程和进程都能实现并发,在java编程领域,线程是实现并发的主要方式 每个进程都有独立的运行环境,内存空间.进程的通信需要通过,pipline或者socket 线程共 ...
- 【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!
本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ...
- 【Java并发基础】并发编程领域的三个问题:分工、同步和互斥
前言 可以将Java并发编程抽象为三个核心问题:分工.同步和互斥. 这三个问题的产生源自对性能的需求.最初时,为提高计算机的效率,当IO在等待时不让CPU空闲,于是就出现了分时操作系统也就出现了并发. ...
- java并发基础(五)--- 线程池的使用
第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...
- java并发基础(二)
<java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ...
- Java并发包下锁学习第二篇Java并发基础框架-队列同步器介绍
Java并发包下锁学习第二篇队列同步器 还记得在第一篇文章中,讲到的locks包下的类结果图吗?如下图: 从图中,我们可以看到AbstractQueuedSynchronizer这个类很重要(在本 ...
- 【Java并发基础】利用面向对象的思想写好并发程序
前言 下面简单总结学习Java并发的笔记,关于如何利用面向对象思想写好并发程序的建议.面向对象的思想和并发编程属于两个领域,但是在Java中这两个领域却可以融合到一起.在Java语言中,面向对象编程的 ...
随机推荐
- java中构造器的调用顺序
在编程的过程中,我们经常会遇到多个类的继承问题,那么多个类的构造器是按照什么顺序调用的呢? 先看一段代码: public class Meal { public Meal() { System.out ...
- java 编写小工具 尝试 学习(四)
1.在新建 的窗口上 ,添加了一个按钮后 ,给 按钮添加一个事件 ,意思就是 点击按钮后 发生什么事情!不废话 贴图 贴 代码! package jFrameDemo; import java.aw ...
- Windows中杀死某个端口对应的进程
netstat -ano | findstr 3000 //列出进程极其占用的端口,且包含 3000 tasklist | findstr 17692 // 查看是什么程序占用的 taskkill - ...
- 提示AttributeError: 'module' object has no attribute 'HTTPSHandler'解决方法
今天在新机器上安装sqlmap,运行提示AttributeError: 'module' object has no attribute 'HTTPSHandler' 网上找了找资料,发现一篇文章ht ...
- Matplotlib中中文不显示问题
我们在使用jupter进行数据分析的时候,会接触到Matplotlib这个库,它是用来进行可视化数据分析的,在一个图中,我们常常会加入一些中文来进行说明.当我们加入中文的时候会出现下图所示的样子: 可 ...
- HDBS之应用代码优化
一.目录结构树 总体概述 代码检测工具sonar HDBS代码优化 总结开发注意点 二.总体概述 进入现在这家公司我的第一个任务就是对HDBS进行代码质量优化.HDBS可能大家不是很了解,现在给大家简 ...
- synchronized 控制并发(活动秒杀)
1.首先我们新建一个Controller用于秒杀: package com.imooc.Controller; import com.imooc.service.impl.SeckillService ...
- asp.net mvc5 step by step(四)——关于Controller的ActionResult
ActionResult是控制器方法执行后返回的结果类型,控制器方法可以返回一个直接或间接从ActionResult抽象类继承的类型,如果返回的 是非ActionResult类型,控制器将会将结果转换 ...
- 浅析Vue原理(部分源码解析)
响应式 Object.defineProperty Object.defineProperty(obj, prop, descriptor) // 对象.属性.描述符 Object.definePro ...
- 【模板】负环(spfa)
题目描述 暴力枚举/SPFA/Bellman-ford/奇怪的贪心/超神搜索 输入输出格式 输入格式: 第一行一个正整数T表示数据组数,对于每组数据: 第一行两个正整数N M,表示图有N个顶点,M条边 ...