Lock接口

Synchronized关键字回顾:

多线程编程步骤(上):

  1. 创建资源类,在资源类创建属性和操作方法
  2. 创建多个线程,调用资源类的操作方法

创建线程的四种方式:

  1. 继承Thread
  2. 实现Runnable接口
  3. 使用Callable接口
  4. 使用线程池

使用synchronized同步实现售票问题:

只有当资源是空闲的时候,线程才能访问。

 /**
  * 创建资源
  */
 class ticket{
     private  int number = 30;
     public synchronized void sell(){
         if(number >0){
             System.out.println(Thread.currentThread().getName()+":卖出"+number--+"剩余:"+number);
        }
 ​
    }
 }
 ​
 /**
  * 创建多个线程,调用资源类的操作方法
  */
 public class sellTicket {
     public static void main(String[] args) {
         /**
          * 创建资源
          */
         ticket ticket = new ticket();
         /**
          * 创建三个线程(售票员)
          */
         new Thread(new Runnable() {
             public void run() {
                 for (int i = 0; i < 40; i++) {
                     ticket.sell();
                }
            }
        }, "aaa").start();
         new Thread(new Runnable() {
             public void run() {
                 for (int i = 0; i < 40; i++) {
                     ticket.sell();
                }
            }
        }, "bbb").start();
         new Thread(new Runnable() {
             public void run() {
                 for (int i = 0; i < 40; i++) {
                     ticket.sell();
                }
            }
        }, "ccc").start();
    }
 }
 ​

学习《Java并发编程的艺术》一节是synchronized的实现原理与应用

Java中的每一个对象都可以作为锁:

  1. 对于同步方法,锁的是当前的对象。
  2. 对于静态方法,锁的是当前类的class对象。
  3. 对于静态代码块,Synchonized括号里配置的对象。

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

那么锁到底存在哪里呢?锁里面会存储什么信息呢?

这个问题是该书中的一个问题,对个人来说有点抽象,主要点是:JVM基于进入和退出Monitor(监视器)对象来实现方法同步和代码块同步,但两者的实现细节不一样。

代码块同步是使用monitorenter 和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有 详细说明。但是,方法的同步同样可以使用这两个指令来实现。

monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结 束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有 一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter 指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

其中monitorenter主要是获取监视器锁,monitorexit主要是释放监视器锁。

synchonized中的一个概念是Java对象头:

synchronized用的锁是存放在Java对象头里面的。

非数组类型 数组类型
虚拟机 3个字宽 2个字宽

需要注意的是:在32位虚拟机中:1字宽=4字节,即:32bit

Java对象头里的Mark Word里面默认存储对象HashCode、分代年龄和锁标记位。

在运行期间,Mark Word里面存储的数据会随着锁标志位的变化而变化。

32位JVM的Mark Word的默认存储结构与在64位JVM不相同

Lock接口并创建实例:

Java并发编程的艺术

在JavaSE5之前使用的是synchronized关键字实现锁功能,5v之后并发包中新增Lock接口以及相关实现类用来实现锁功能,提供与synchronized类似的同步关系,但是比其更具有灵活性和扩展性(拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁)。

查看Lock的文档得知:

使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下:

  class X {
    private final ReentrantLock lock = new ReentrantLock();
    // ...
 ​
    public void m() {
      lock.lock();  // block until condition holds
      try {
        // ... method body
      } finally {
        lock.unlock()
      }
    }
  }
 

根据上述的代码,实现买票代码:

 import java.util.concurrent.locks.ReentrantLock;
 class ticket2{
     private int number = 30;
 ​
     //创建一个重入锁
     private ReentrantLock lock = new ReentrantLock();
 ​
     public void sale(){
         //在内容之前加锁
         lock.lock();
         if(number >0){
             System.out.println(Thread.currentThread().getName()+":卖出"+number--+"剩余:"+number);
        }
         //在内容之后解锁
         lock.unlock();
    }
 }
 ​
 public class lockCase {
     public static void main(String[] args) {
         ticket2 tk = new ticket2();
         //创建线程
         new Thread(()->{
             for (int i = 0; i < 40; i++) {
                 tk.sale();
            }
        },"aaa").start();
         new Thread(()->{
             for (int i = 0; i < 40; i++) {
                 tk.sale();
            }
        },"bbb").start();
         new Thread(()->{
             for (int i = 0; i < 40; i++) {
                 tk.sale();
            }
        },"bbb").start();
    }
 }

上述代码中,在ticket2类中存在一个问题就是,当lock中间内容如果出现报错,那么后面的代码无法执行,也就是锁无法释放。所以我们需要将unlock()放到finally中。

目的是保证在获取到锁之后,最终能够被释放。

 public void sale(){
         //在内容之前加锁
         lock.lock();
         try{
             if(number >0){
                 System.out.println(Thread.currentThread().getName()+":卖出"+number--+"剩余:"+number);
            }
             //在内容之后解锁
        }finally{
             lock.unlock();
        }
 ​
    }

JUC之Lock接口以及Synchronized回顾的更多相关文章

  1. Java并发之synchronized关键字和Lock接口

    欢迎点赞阅读,一同学习交流,有疑问请留言 . GitHub上也有开源 JavaHouse,欢迎star 引用 当开发过程中,我们遇到并发问题.怎么解决? 一种解决方式,简单粗暴:上锁.将千军万马都给拦 ...

  2. synchronized关键字,Lock接口以及可重入锁ReentrantLock

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  3. Java基础知识强化之多线程笔记06:Lock接口 (区别于Synchronized块)

    1. 简介 我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式 ...

  4. 多线程里面的关键字,wait, notfiy, 锁(synchronized), lock接口

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  5. java多线程Lock接口简介使用与synchronized对比 多线程下篇(三)

    前面的介绍中,对于显式锁的概念进行了简单介绍 显式锁的概念,是基于JDK层面的实现,是接口,通过这个接口可以实现同步访问 而不同于synchronized关键字,他是Java的内置特性,是基于JVM的 ...

  6. 线程中synchronized关键字和lock接口的异同

    一.synchronized关键字 1.可以用来修饰代码块 synchronized (this) { // 同步的关键字 this 表示当前线程对象 if (num == 0) { break; } ...

  7. Synchronized和Lock接口

    关于synchronized字段,不管该关键字是修饰方法还是修饰同步代码块,synchronzed拿到的都是对象. 当synchronized修饰的是方法时,synchronized所拿到的是调用该方 ...

  8. JUC之Callable接口回顾和JUC辅助类

    Callable接口和JUC辅助类 Callable接口: 回顾: 创建线程的四种方式: 继承Thread 实现runnable接口 实现callable接口 使用线程池 之前的文章:多线程编程1-定 ...

  9. 5.Lock接口及其实现ReentrantLock

    jdk1.7.0_79 在java.util.concurrent.locks这个包中定义了和synchronized不一样的锁,重入锁——ReentrantLock,读写锁——ReadWriteLo ...

随机推荐

  1. [cf1421E]Swedish Heroes

    令$p_{i}$为最终$a_{i}$之前的系数($p_{i}\in \{-1,1\}$),则有$n+\sum_{i=1}^{n}[p_{i}=-1]\equiv 1(mod\ 3)$ 证明:对于两个满 ...

  2. [bzoj1432]Function

    对于这n个函数,构成了$n(n-1)/2$个交点,对交点离散后,相邻两个交点间函数的编号构成了一个排列,而每一个排列第i个数所构成的段数就是第i层的段数不妨设初始在-oo处这个排列是1,2,--,n, ...

  3. 【Tool】JDK8 安装

    JDK8 2019-07-26  14:05:21  by冲冲 1. 下载 通常前往官网 https://www.oracle.com/technetwork/java/javase/download ...

  4. 记一次 android 线上 oom 问题

    背景 公司的主打产品是一款跨平台的 App,我的部门负责为它提供底层的 sdk 用于数据传输,我负责的是 Adnroid 端的 sdk 开发. sdk 并不直接加载在 App 主进程,而是隔离在一个单 ...

  5. 卸载.net 5.0后使用dotnet提示Found .NET Core SDK

    之前安装了预览版本的vs2019后试了下,然后卸载了.但发现控制台执行dotnet相关命令提示Found .NET Core SDK, but did not find dotnet.dll at [ ...

  6. 一类巧妙利用利用失配树的序列DP

    I.导入 求长度为\(\text{len}\)的包含给定连续子串\(\text{T}\)的 0/1 串的个数.(\(|T|<=15\)) 通常来说这种题目应该立刻联想到状压 DP 与取反集--这 ...

  7. Linux内网时钟同步问题(ntp和chrony)

    我们都知道时钟同步可以使用外网服务器,在内网内不能连接外网的时候也需要时钟同步,那怎么进行呢? 选择内网的一台稳定的服务器作为时钟源,然后让其他机器都来同步这台机器即可. 注:其实ntp服务和chro ...

  8. Oracle基础入门

    说明:钓鱼君昨天在网上找到一份oracle项目实战的文档,粗略看了一下大致内容,感觉自己很多知识不够扎实,便跟着文档敲了一遍,目前除了机械性代码没有实现外,主要涉及知识:创建表空间.创建用户.给用户赋 ...

  9. 小程序https启用tls1.2

    公司的web服务器是iis7,在开发微信小程序的时候,需要启用TLS1.2. 将下面的代码复制到文本,存为reg文档,双击搞定. Windows Registry Editor Version 5.0 ...

  10. 开始读 Go 源码了

    原文链接: 开始读 Go 源码了 学完 Go 的基础知识已经有一段时间了,那么接下来应该学什么呢?有几个方向可以考虑,比如说 Web 开发,网络编程等. 在下一阶段的学习之前,写了一个开源项目|Go ...