Object中对内置锁进行操作的一些方法:

Java内置锁通过synchronized关键字使用,使用其修饰方法或者代码块,就能保证方法或者代码块以同步方式执行.

内置锁使用起来非常方便,不需要显式的获取和释放,任何一个对象都能作为一把内置锁。使用内置锁能够解决大部分的同步场景。“任何一个对象都能作为一把内置锁”也意味着出现synchronized关键字的地方,都有一个对象与之关联,具体说来:

  • 当synchronized作用于普通方法是,锁对象是this;
  • 当synchronized作用于静态方法是,锁对象是当前类的Class对象;
  • 当synchronized作用于代码块时,锁对象是synchronized(obj)中的这个obj。

wait()系列:

wait()系列方法的作用是:使当前已经获得该对象锁的线程进入等待状态,并且释放该对象的锁。

notify()系列:

notify()系列方法的作用是:唤醒那些正在等待该对象锁的线程,使其继续运行。

基于wait() notify()机制,我们可以实现一个简易的生产者-消费者模型。

大体思路如下,一个生产者线程负责向一个仓库中存放(put)物品,一个消费者线程负责从仓库中取出(get)物品。

public class Warehouse {private Queue<Integer> queue;private int capacity;public Warehouse(int capacity) {this.capacity = capacity;queue = new LinkedList();}public synchronized void put(int num) {if (queue.size() >= capacity) {try {System.out.println(Thread.currentThread().getName() + " , put full wait");wait();} catch (InterruptedException e) {e.printStackTrace();}}queue.add(num);System.out.println(Thread.currentThread().getName() + " , put : " + num + "  , queue -> " + queue);notifyAll();}public synchronized int get() {if (queue.isEmpty()) {try {System.out.println(Thread.currentThread().getName() + " , get empty wait");wait();} catch (InterruptedException e) {e.printStackTrace();}}int num = queue.poll();System.out.println(Thread.currentThread().getName() + " , get : " + num + "  , queue -> " + queue);notifyAll();return num;}
}

public static void main(String[] args) {Warehouse warehouse = new Warehouse(4);Random random = new Random();new Thread(new Runnable() {@Overridepublic void run() {while (true) {warehouse.put(random.nextInt(10));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}, "生产者-01").start();new Thread(new Runnable() {@Overridepublic void run() {while (true) {warehouse.get();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}}, "消费者-01").start();}

显式锁

内置锁这么好用,为什么还需多出一个显式锁呢?因为有些事情内置锁是做不了的,比如:

  1. 我们想给锁加个等待时间超时时间,超时还未获得锁就放弃,不至于无限等下去;
  2. 我们想以可中断的方式获取锁(线程可以被打断),这样外部线程给我们发一个中断信号就能唤起等待锁的线程;
  3. 我们想为锁维持多个等待队列,比如一个生产者队列,一个消费者队列,一边提高锁的效率。

显式锁(ReentrantLock)正式为了解决这些灵活需求而生。ReentrantLock的字面意思是可重入锁,可重入的意思是线程可以同时多次请求同一把锁而不会自己导致自己死锁。下面是内置锁和显式锁的区别:

  • 可定时RenentrantLock.tryLock(long timeout, TimeUnit unit)提供了一种以定时结束等待的方式,如果线程在指定的时间内没有获得锁,该方法就会返回false并结束线程等待。

  • 可中断:你一定见过InterruptedException,很多跟多线程相关的方法会抛出该异常,这个异常并不是一个缺陷导致的负担,而是一种必须,或者说是一件好事。可中断性给我们提供了一种让线程提前结束的方式(而不是非得等到线程执行结束),这对于要取消耗时的任务非常有用对于内置锁,线程拿不到内置锁就会一直等待,除了获取锁没有其他办法能够让其结束等待RenentrantLock.lockInterruptibly()给我们提供了一种以中断结束等待的方式。

  • 条件队列(condition queue):线程在获取锁之后,可能会由于等待某个条件发生而进入等待状态(内置锁通过Object.wait()方法,显式锁通过Condition.await()方法),进入等待状态的线程会挂起并自动释放锁,这些线程会被放入到条件队列当中。synchronized对应的只有一个条件队列,而ReentrantLock可以有多个条件队列,多个队列有什么好处呢?请往下看

  • 条件谓词:线程在获取锁之后,有时候还需要等待某个条件满足才能做事情,比如生产者需要等到“缓存不满”才能往队列里放入消息,而消费者需要等到“缓存非空”才能从队列里取出消息。这些条件被称作条件谓词,线程需要先获取锁,然后判断条件谓词是否满足,如果不满足就不往下执行,相应的线程就会放弃执行权并自动释放锁。使用同一把锁的不同的线程可能有不同的条件谓词,如果只有一个条件队列,当某个条件谓词满足时就无法判断该唤醒条件队列里的哪一个线程;但是如果每个条件谓词都有一个单独的条件队列,当某个条件满足时我们就知道应该唤醒对应队列上的线程(内置锁通过Object.notify()或者Object.notifyAll()方法唤醒,显式锁通过Condition.signal()或者Condition.signalAll()方法唤醒)。这就是多个条件队列的好处。

使用内置锁时,对象本身既是一把锁又是一个条件队列;使用显式锁时,RenentrantLock的对象是锁,条件队列通过RenentrantLock.newCondition()方法获取,多次调用该方法可以得到多个条件队列

一个使用显式锁的典型示例如下:

// 显式锁的使用示例
ReentrantLock lock = new ReentrantLock();// 获取锁,这是跟synchronized关键字对应的用法。
lock.lock();
try{// your code
}finally{lock.unlock();
}// 可定时,超过指定时间为得到锁就放弃
try {lock.tryLock(10, TimeUnit.SECONDS);try {// your code}finally {lock.unlock();}
} catch (InterruptedException e1) {// exception handling
}// 可中断,等待获取锁的过程中线程线程可被中断
try {lock.lockInterruptibly();try {// your code}finally {lock.unlock();}
} catch (InterruptedException e) {// exception handling
}// 多个等待队列,具体参考[ArrayBlockingQueue](https://github.com/CarpenterLee/JCRecipes/blob/master/markdown/ArrayBlockingQueue.md)
/** Condition for waiting takes */
private final Condition notEmpty = lock.newCondition();
/** Condition for waiting puts */
private final Condition notFull = lock.newCondition();

注意,上述代码将unlock()放在finally块里,这么做是必需的。显式锁不像内置锁那样会自动释放,使用显式锁一定要在finally块中手动释放,如果获取锁后由于异常的原因没有释放锁,那么这把锁将永远得不到释放!将unlock()放在finally块中,保证无论发生什么都能够正常释放。

内置锁能够解决大部分需要同步的场景,只有在需要额外灵活性是才需要考虑显式锁,比如可定时、可中断、多等待队列等特性。

显式锁虽然灵活,但是需要显式的申请和释放,并且释放一定要放到finally块中,否则可能会因为异常导致锁永远无法释放!这是显式锁最明显的缺点。

综上,当需要同步时请优先考虑更安全的更易用的隐式锁。

package com.imooc.locks;import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Task {private final Lock lock = new ReentrantLock();private final Condition addCondition = lock.newCondition();private final Condition subCondition = lock.newCondition();private static int num = 0;private List<String> lists = new LinkedList<String>();public void add() {lock.lock();try {while(lists.size() == 10) {//当集合已满,则"添加"线程等待addCondition.await();}num++;lists.add("add Banana" + num);System.out.println("The Lists Size is " + lists.size());System.out.println("The Current Thread is " + Thread.currentThread().getName());System.out.println("==============================");this.subCondition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {//释放锁lock.unlock();}}public void sub() {lock.lock();try {while(lists.size() == 0) {//当集合为空时,"减少"线程等待subCondition.await();}String str = lists.get(0);lists.remove(0);System.out.println("The Token Banana is [" + str + "]");System.out.println("The Current Thread is " + Thread.currentThread().getName());System.out.println("==============================");num--;addCondition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}

转载于:https://www.cnblogs.com/liufei1983/p/8120395.html

七 内置锁 wait notify notifyall; 显示锁 ReentrantLock相关推荐

  1. 解决 IntelliJ IDEA 内置的 Tomcat 日志中显示的中文乱码

    解决 IntelliJ IDEA 内置的 Tomcat 日志中显示的中文乱码 方法 1 方法 2(不推荐) 笔者的环境: JDK 13.0.2 Maven 3.6.3 Tomcat 9.0.41(Se ...

  2. 用 texstudio, 外部 pdf 浏览器查看可以正常显示中文, 但是内置的 pdf 浏览器不能显示中文?

    文章目录 第一种方法 下面是总结一下: 关于 `poppler` 的依赖: 具体的步骤 第二种方法 第一种方法 这个问题见知乎的帖子: 用texstudio,外部pdf浏览器查看可以正常显示中文,但是 ...

  3. SK6805MICRO-2427RGB灯珠 2427RGB内置IC灯珠 适用显示领域、智能应用、蓝牙WiFi装饰

    深圳市日不落灯光科技有限公司专业内置芯片LED及方案产品开发.专业生产4020侧发光/SK6812.6822/2813断点续传LED.4020侧发光LED.2427RGB/3535MINI小尺寸封装等 ...

  4. JDK内置并发AQS同步器的独占锁获取与释放

    作者简介:笔名seaboat,擅长工程算法.人工智能算法.自然语言处理.计算机视觉.架构.分布式.高并发.大数据和搜索引擎等方面的技术,大多数编程语言都会使用,但更擅长Java.Python和C++. ...

  5. javascript的原型,原型链,内置对象 拖曳对话框 放大镜显示和隐藏遮挡层及大层

    图片跟着鼠标飞 1.<!DOCTYPE html><html lang="en"> <head> <meta charset=" ...

  6. mac 菜单栏图标删除_您可以在Mac菜单栏上显示的所有内置图标(可能)

    mac 菜单栏图标删除 Apple's operating system's menu bar is truly old school; it's been around for as long as ...

  7. TexStudio内置pdf无法显示中文字体 适用于M1 Macbook

    TexStudio内置pdf无法显示中文,外部pdf正常,知乎帖子已有非常有用的建议,见用texstudio,外部pdf浏览器查看可以正常显示中文,但是内置的pdf浏览器不能显示中文? 此处记录一下, ...

  8. java 显示锁_Java 实现一个自己的显式锁Lock(有超时功能)

    Lock接口 package concurency.chapter9; import java.util.Collection; public interface Lock { static clas ...

  9. Java中的显示锁 ReentrantLock 和 ReentrantReadWriteLock

    在Java1.5中引入了两种显示锁,分别是可重入锁ReentrantLock和可重入读写锁ReentrantReadWriteLock.它们分别实现接口Lock和ReadWriteLock.(注意:s ...

最新文章

  1. 图灵奖得主Judea Pearl 智源大会演讲:从“大数据革命”到“因果革命”
  2. 利用归并排序求逆序对
  3. 计算机操作系统(八)---虚拟存储器
  4. TCL通讯将刊行代表1.09亿股的台湾存托凭据
  5. 阿里首席架构师科普RPC框架是什么
  6. C#学生信息管理系统
  7. 【HDU - 5015 】233 Matrix (矩阵快速幂)
  8. 图像处理之调整亮度与饱和度
  9. 直线分割平面的公式_直线分割平面-jiangwen127-ChinaUnix博客
  10. VSCode 使用 StandardJS 自动格式化代码
  11. 浅谈运营商行业业务的发展方向
  12. crx插件转换火狐插件_关于Firefox插件
  13. js 判断 服务器连接状态,原生JS判断网站服务器是否开启问题及解决方案
  14. Docker Redis远程主机强迫关闭了一个现有的连接
  15. 考研失败最根本的5个原因!
  16. 第七届蓝桥杯大赛个人赛--小明被绑架到X星球的巫师W那里
  17. JSR303 数据效验
  18. ,进门安卓手机首选 三星盖世S5670评测
  19. 用HTML5绘制的一个星空特效图
  20. 分布式系统:一致性hash算法 在分布式系统中的应用

热门文章

  1. 51nod 1073约瑟夫环
  2. IKAnalyzer进行中文分词和去停用词
  3. ABAP中创建动态内表的三种方法(转载)
  4. FlashFXP使用教程
  5. 设计模式笔记(7)---适配器模式(结构型)
  6. [转载]C#中,让组合框(ComboBox)支持拼音首字母检索筛选
  7. 自己改造 VSPaste 插件
  8. 【洛谷 P1659】 [国家集训队]拉拉队排练(manacher)
  9. Java这些多线程基础知识你会吗?
  10. laravel 导出插件