Synchronized & Lock

  • synchronized 是Java语言中的关键字,由monitorenter,monitorexit两个指令实现。JVM会将monitorenter指定插在同步代码块开始的地方,将monitorexit指定插在同步代码快结束和出现异常的地方。

  • Lock是JUC包下的组件, 是基于AQS(队列同步器)实现的。

  • synchronized功能与ReentrantLock类相对应, 都是可重入的锁。

  • Lock与synchronized关键字相比,实现了公平锁和非公平锁,synchronized关键字是非公平的。同时Lock接口提供了在获取锁被阻塞时可响应中断以及超时获取锁的API。

  • Lock接口可以绑定多个条件,即绑定多个 Condition 对象,这样唤醒时可以唤醒指定条件上的线程。如生产者消费者例子,使用 synchronized 关键字时,当生产者生产消息时,需要唤醒消费者线程,我们只能调用notifyAll 方法,这样不仅会唤醒消费者端的线程而且还会唤醒生产者自己这一端的线程。

**注: **

  1. 公平锁是指多个线程等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁,而非公平锁不保证这一点。在锁被释放时,任何一个等待锁的线程都有机会获取锁。
  2. ReentrantLock实现的非公平锁只能保证阻塞队列里最早等待的线程与新来的线程竞争抢锁, 对于阻塞队列其它的线程依然需要等待,因为队列是先进先出的,只有该线程获取锁然后释放锁后后续节点才有资格竞争锁。

Lock 接口的 API

public interface Lock {

    // 获取锁直到获取锁成功后返回,否则被阻塞,即使被中断也不会返回
void lock(); // 获取锁并且响应中断,注意如果t线程因为竞争锁失败而被阻塞,另外一个线程中断了t线程
// 那么t线程会被唤醒,并且抛出中断异常,清除中断状态, 阻塞线程的方法是调用LockSupport.park()
// 当调用了LockSupport.unpark或者中断了线程,线程会从park方法中唤醒。
void lockInterruptibly() throws InterruptedException; // 尝试获取锁,不管成功与否都返回
boolean tryLock(); // 超时获取锁,此方法返回的情况如下:
// 1. 在给定时间内竞争到锁, 返回true
// 2. 超时时间过了, 返回false
// 3. 线程被中断,抛出中断异常,清除中断状态
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 释放锁,如果当前线程没有获取到锁,调用此方法将会抛出IllegalMonitorStateException
void unlock(); // 创建Condition对象,提供了类似synchronized 锁对象的wait, notify, notifyAll方法
Condition newCondition();
} public interface Condition { // 使获取锁的线程阻塞,并且释放锁,直到另一个线程中断了此线程或者调用了signal,signalAll方法
// 没有获取锁的线程调用此方法会抛出IllegalMonitorStateException
void await() throws InterruptedException; // 使获取的线程阻塞,并且释放锁,但是不响应中断请求,只有调用了signal,signalAll才会被唤醒
// 其实是线程中断后醒过来,再次被阻塞-LockSupport.park()
void awaitUninterruptibly(); // 使获取的线程阻塞,并且释放锁, 被唤醒的原因如下:
// 1. 超时时间已过 2. 线程被中断 3. 有线程调用了signal,signalAll方法
boolean await(long time, TimeUnit unit) throws InterruptedException; // 唤醒最开始等待在条件队列上的线程
void signal(); // 唤醒所有等待在条件队列上的线程
void signalAll(); }

注: 所有从await中醒过来的线程只有重新获取到锁才能往下执行,否则依然会在同步队列中等待获取锁

Lock的使用

下面将使用ReentrantLock实现一个简单的阻塞队列。

public class BlockQueue {
private Lock lock = new ReentrantLock(); private Condition full = lock.newCondition(); private Condition empty = lock.newCondition(); private Queue<String> queue; private int capacity; public BlockQueue(int capacity) {
queue = new ArrayDeque<>(capacity);
this.capacity = capacity;
} public void put(String element) {
lock.lock();
try {
while (queue.size() == capacity) {
try {
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.add(element);
empty.signalAll();
} finally {
lock.unlock();
}
} public String take() {
lock.lock();
try {
while (queue.isEmpty()) {
try {
empty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String element = queue.remove();
full.signalAll();
return element;
} finally {
lock.unlock();
}
}
}

测试Lock接口的方法

public class LockTest {

    private final Object lock = new Object();

    /**
* 测试synchronized关键字
* synchronized: 当线程在获取锁时, 竞争失败的线程会处于阻塞, 并且不响应中断直至获取到锁
*
* 程序结果:
* --------------主线程准备释放锁---------------------
* 是否被中断: true
* block-thread获取锁成功
*/
@Test
public void testSynchronized() {
// 当前线程持有锁
synchronized (lock) {
// 开启另外一个线程尝试获取锁
Thread t = new Thread(()->{
//阻塞
synchronized (lock) {
System.out.println("是否被中断: " + Thread.currentThread()
.isInterrupted());
System.out.println(Thread.currentThread().getName() + "获取锁成功");
}
}, "block-thread");
t.start(); //休眠一秒, 让开启的线程充分运行, 接着进行中断
sleep(1);
t.interrupt(); //长久睡眠不释放锁, 用来观察结果, 看看线程t是否会响应中断
sleep(10);
System.out.println("--------------主线程准备释放锁---------------------");
}
} /**
* 测试lock.lock()同synchronized关键字
*
* 程序结果:
* --------------主线程准备释放锁---------------------
* 是否被中断: true
* block-thread获取锁成功
*/
@Test
public void testLock() {
//当前线程持有锁
Lock lock = new ReentrantLock();
lock.lock();
try {
// 开启另外一个线程尝试获取锁
Thread t = new Thread(()->{
//阻塞
lock.lock();
try {
System.out.println("是否被中断: " + Thread.currentThread()
.isInterrupted());
System.out.println(Thread.currentThread().getName() + "获取锁成功");
} finally {
lock.unlock();
} }, "block-thread");
t.start(); //休眠一秒, 让开启的线程充分运行, 接着进行中断
sleep(1);
t.interrupt(); //长久睡眠不释放锁, 用来观察结果, 看看线程t是否会响应中断
sleep(10);
System.out.println("--------------主线程准备释放锁---------------------");
} finally {
lock.unlock();
}
} /**
* 测试lock.lockInterruptibly(): 抛出中断异常
* 当线程被中断后,线程会从LockSupport.park()中醒过来,然后会检查自己是否被中断,如果被中断过
* 则抛出中断异常,清除中断状态。
*
* 程序结果: block-thread is interrupted! exit
*/
@Test
public void testlockInterruptibly() {
// 当前线程持有锁
ReentrantLock lock = new ReentrantLock();
try {
lock.lock();
// 开启另外一个线程尝试获取锁
Thread t = new Thread(()->{
//阻塞
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "获取锁成功");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +
"is interrupted! exit");
} finally {
if(lock.isHeldByCurrentThread())
lock.unlock();
} }, "block-thread");
t.start(); //休眠一秒, 让开启的线程充分运行, 接着进行中断
sleep(1);
t.interrupt(); //长久睡眠不释放锁, 用来观察结果, 看看线程t是否会相应中断
sleep(10);
} finally {
lock.unlock();
}
} private void sleep(int second) {
try {
TimeUnit.SECONDS.sleep(second);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Java中的Lock接口的更多相关文章

  1. Java中的Lock与synchronized

    并发编程学习笔记之Lock与synchronized 一.什么是可重入锁 Lcok在Java中是一个接口,一般在面试问题中问到的可能是ReentrantLock与synchronized的区别.Ree ...

  2. Java 中的集合接口——List、Set、Map

    Java 中的集合接口——List.Set.Map 什么叫集合:集合就是Java API所提供的一系列类的实例,可以用于动态存放多个对象.这跟我们学过的数组差不多,那为什么我们还要学集合,我们看看数组 ...

  3. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

  4. 关于JAVA中抽象类和接口的区别辨析

    今天主要整理一下新学习的有关于Java中抽象类和接口的相关知识和个人理解. 1 抽象类 用来描述事物的一般状态和行为,然后在其子类中去实现这些状态和行为.也就是说,抽象类中的方法,需要在子类中进行重写 ...

  5. java中的标记接口(标签接口)

    Java中的标记接口(Marker Interface),又称标签接口(Tag Interface),具体是不包含任何方法的接口.在Java中很容易找到标记接口的例子,比如JDK中的Serialzab ...

  6. Java中的Serializable接口和transient关键字

    Java中的Serializable接口和transient关键字 Table of Contents 1. 向memcached中放数据时遇到NotSerializableException异常 2 ...

  7. 一文带你看懂Java中的Lock锁底层AQS到底是如何实现的

    前言 相信大家对Java中的Lock锁应该不会陌生,比如ReentrantLock,锁主要是用来解决解决多线程运行访问共享资源时的线程安全问题.那你是不是很好奇,这些Lock锁api是如何实现的呢?本 ...

  8. 用好JAVA中的函数式接口,轻松从通用代码框架中剥离掉业务定制逻辑

    大家好,又见面了. 今天我们一起聊一聊JAVA中的函数式接口.那我们首先要知道啥是函数式接口.它和JAVA中普通的接口有啥区别?其实函数式接口也是一个Interface类,是一种比较特殊的接口类,这个 ...

  9. Java Concurrency API 中的 Lock 接口(Lock interface) 是什么?对比同步它有什么优势?

    Lock 接口比同步方法和同步块提供了更具扩展性的锁操作. 他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的 条件对象. 它的优势有: 可以使锁更公平 可以使线程在等待锁的时候响 ...

随机推荐

  1. SSH登录启用Google二次身份验证

    一般来说,使用ssh远程登录服务器,只需要输入账号和密码,显然这种方式不是很安全.为了安全着想,可以使用GoogleAuthenticator(谷歌身份验证器),以便在账号和密码之间再增加一个验证码, ...

  2. 深度学习VS机器学习——到底什么区别

    转自:https://baijiahao.baidu.com/s?id=1595509949786067084&wfr=spider&for=pc 最近在听深度学习的课,老师提了一个基 ...

  3. Linux网络编程学习(五) ----- 信号(第四章)

    1.基本概念 进程阻塞: 进程执行条件得不到满足,就自动放弃CPU资源而进入休眠状态,以等待条件满足,当条件满足时,系统就将控制权还给该进程进行未完成的操作 共享资源: 进程间协调使用的系统资源 锁定 ...

  4. Linux网络编程学习(四) -----守护进程的建立(第三章)

    本文介绍一个例程daemon_init() #include <sys/types.h> #include <signal.h> #include <unistd.h&g ...

  5. MFC笔记8

    1.在循环使用数组时需要清理数组 CString str; memset(str,0,strlen(str)); 判断两个字符串包含数字大小是否相等 CString str="22" ...

  6. eclipse git(版本回退)

    https://www.cnblogs.com/duex/p/6389999.html

  7. Java 虚拟机概述

    虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机有自己完善的硬体架构,如处理器.堆栈.寄存器等,还具有相应的指令系统.Java虚拟机屏蔽了与具体操作系统平 ...

  8. jstl标准标签库 常用标签

    JSTL(JSP Standard Tag Library)标准标签库: 1, 核心标签(最常用, 最重要的) 表达式控制标签 out 输出常量 value---直接赋值 输出变量 default-- ...

  9. 通过msyql proxy链接mysql中文乱码及session问题

    1.session问题 问题前提:一台机数据库为两个实例,通过不同的socket监听不同端口对外提供服务.不同的站点都访问同一个VIP不同的端口进行访问数据库. 故障现象:一旦有一个站点先用了这个vi ...

  10. Working days

    form FRM_GET_WORKING_DAYS TABLES pt_days CHANGING pv_duration. DATA:ls_xt001w TYPE t001w, lv_sdate T ...