JUC之Lock接口以及Synchronized回顾
Lock接口
Synchronized关键字回顾:
多线程编程步骤(上):
- 创建资源类,在资源类创建属性和操作方法
- 创建多个线程,调用资源类的操作方法
创建线程的四种方式:
- 继承Thread
- 实现Runnable接口
- 使用Callable接口
- 使用线程池
使用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中的每一个对象都可以作为锁:
- 对于同步方法,锁的是当前的对象。
- 对于静态方法,锁的是当前类的class对象。
- 对于静态代码块,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回顾的更多相关文章
- Java并发之synchronized关键字和Lock接口
欢迎点赞阅读,一同学习交流,有疑问请留言 . GitHub上也有开源 JavaHouse,欢迎star 引用 当开发过程中,我们遇到并发问题.怎么解决? 一种解决方式,简单粗暴:上锁.将千军万马都给拦 ...
- synchronized关键字,Lock接口以及可重入锁ReentrantLock
多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...
- Java基础知识强化之多线程笔记06:Lock接口 (区别于Synchronized块)
1. 简介 我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式 ...
- 多线程里面的关键字,wait, notfiy, 锁(synchronized), lock接口
多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...
- java多线程Lock接口简介使用与synchronized对比 多线程下篇(三)
前面的介绍中,对于显式锁的概念进行了简单介绍 显式锁的概念,是基于JDK层面的实现,是接口,通过这个接口可以实现同步访问 而不同于synchronized关键字,他是Java的内置特性,是基于JVM的 ...
- 线程中synchronized关键字和lock接口的异同
一.synchronized关键字 1.可以用来修饰代码块 synchronized (this) { // 同步的关键字 this 表示当前线程对象 if (num == 0) { break; } ...
- Synchronized和Lock接口
关于synchronized字段,不管该关键字是修饰方法还是修饰同步代码块,synchronzed拿到的都是对象. 当synchronized修饰的是方法时,synchronized所拿到的是调用该方 ...
- JUC之Callable接口回顾和JUC辅助类
Callable接口和JUC辅助类 Callable接口: 回顾: 创建线程的四种方式: 继承Thread 实现runnable接口 实现callable接口 使用线程池 之前的文章:多线程编程1-定 ...
- 5.Lock接口及其实现ReentrantLock
jdk1.7.0_79 在java.util.concurrent.locks这个包中定义了和synchronized不一样的锁,重入锁——ReentrantLock,读写锁——ReadWriteLo ...
随机推荐
- 力扣 - 剑指 Offer 54. 二叉搜索树的第k大节点
题目 剑指 Offer 54. 二叉搜索树的第k大节点 思路1 二叉搜索树的特性就是中序遍历结果为递增序列,而题目要求的是第 k 大节点,所以就应该是要遍历结果为降序, 按照先遍历左子树.输出节点.遍 ...
- 『学了就忘』Linux文件系统管理 — 59、使用fdisk命令进行手工分区
目录 1.手工分区前提 (1)要有一块新的硬盘 (2)在虚拟机中添加一块新硬盘 2.手工分区 (1)查看Linux系统所有硬盘及分区 (2)手工分区:详细步骤 (3)保存手工分区 3.硬盘格式化 4. ...
- [NOIP2011 提高组] 观光公交
考虑这类每次都有一类物品贡献相同的物品,求使用了 \(k\) 个物品的最优值,则有考虑考虑贪心. 每次找到一个车到的时间\(>\)最后一个人到的时间,那么找一个覆盖个数最大的地方使用它.
- Codeforces 1361C - Johnny and Megan's Necklace(欧拉回路)
Codeforces 题目传送门 & 洛谷题目传送门 u1s1 感觉这个题作为 D1C 还是蛮合适的-- 首先不难发现答案不超过 \(20\),所以可以直接暴力枚举答案并 check 答案是否 ...
- python-django-事务使用
其中任何一个步骤失败都不行, 失败则回滚 在一对一的模型下 例如,用户基本信息和用户详细信息 必须两个一起添加用户信息才算成功 通过位置参数来实现 手动控制事务 def page_trans_hand ...
- CQC认证与3C认证的区别是什么
中国CCC认证为中国的强制性认证(什么产品能做,CQC网上都要目录), CQC是自愿性的,(不能做CCC认证的产品,可以选择做CQC认证) 产品想要在中国市场上流通,特别是进入超市,商场,招标工厂等的 ...
- Linux—yum的python版本错误——高级解决方案
彻底搞明白,python升级后,为什么会导致yum不可用 首先我们来分析下,python升级后,yum为什么会不可用? 先说个关于python的问题,Linux系统很多软件都依赖于python,因此不 ...
- urllib的基本使用介绍
1. urllib中urlopen的基本使用介绍 1 ### urllib中urlopen的基本使用介绍 2 3 ## urlopen的基本用法(GET请求) 4 import urllib.requ ...
- 【.Net】使用委托实现被引用的项目向上级项目的消息传递事件
前言:在实际项目过程中,经常可能遇到被引用的项目要向上传递消息,但是又不能通过方法进行返回等操作,这个时候委托就派上用场了.以下使用委托,来实现被引用的项目向上传递消息的小教程,欢迎各位大佬提供建议. ...
- 日常Java 2021/9/29
StringBuffer方法 public StringBuffer append(String s) 将指定的字符串追加到此字符序列. public StringBuffer reverse() 将 ...