JUC之多线程锁问题
多线程锁
8种问题锁状态:
该部分全部围绕的是以下内容并结合相应的例子:synchronized实现同步的基础:Java中每个对象都可以作为锁。
具体表现为以下三种形式:(之前只是简单的了解)
- 对于普通同步方法,锁是当前实例对象。
- 对于静态同步方法,锁是当前类的Class对象。
- 对于同步方法块,锁是Synchonized括号里配置的对象
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁
也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通方法必须等待获取锁的方法释放锁后才能获取锁
,可是别的实例对象的非静态同步方法因为跟该实例对象的普通同步方法用的是不同的锁,所以必须等待该实例对象已获取锁的普通同步方法释放锁就可以获取他们自己的锁。
所以的静态同步方法用的也是同一把锁---类对象本身,这两把锁(this/class)是不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞争条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获得锁,而不管是同一实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们是同一个类的实例对象。
具体的例子如下:
package com.JUC;
import java.util.concurrent.TimeUnit;
class phone{
public static synchronized void sendEmail() throws Exception {
// Thread.sleep(4000);
//暂定4s
TimeUnit.SECONDS.sleep(1);
System.out.println("sendEmail-------");
}
public synchronized void sendMessage() throws Exception {
System.out.println("sendSMS----------");
}
public void hello(){
System.out.println("hello wold");
}
}
public class lockPhenomenon {
public static void main(String[] args) throws InterruptedException {
//phone 是模板--class,new phone --this
phone phone = new phone();
phone phone2 = new phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"AAA").start();
Thread.sleep(100);
new Thread(()->{
try {
// phone.sendMessage();
//phone.hello();
phone2.sendMessage();
} catch (Exception e) {
e.printStackTrace();
}
},"BBB").start();
}
}
/**
- 多线程8锁
- 标准访问,先打印邮件还是短信
- sendEmail-------
- sendSMS----------
- 2、邮件方法暂停4s,请问先打印邮件还是短信
- sendEmail-------
- sendSMS----------
- 解释1-2问题:synchronized锁的是当前类对象,一个对象里面如果有多个synchronized方法,在某个时刻内,只要一个线程去调用其中的一个synch方法了,其他线程都得只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法,锁的是当前对象this,被锁定后,其他的线程都不能进入到当前对象的其他synchronized方法。
- 3、新增一个普通方法hello,先打印邮件还是hello.
- hello wold
- sendEmail-------
解释:加个普通方法后发现和同步锁无关,- 4、两部手机,先打印邮件还是先打印短信。
- sendSMS----------
- sendEmail-------
- 解释4:换成两个对象后,不是同一把锁了,情况立刻变化
- 5、两个静态同步方法,同一部手机,请问先打印邮件还是短信
- sendEmail-------
- sendSMS----------
- 6、两个静态同步方法,两部手机,请问先打印邮件还是短信
- sendEmail-------
- sendSMS----------
- 解释5-6问题:都换成静态同步方法后,情况又发生了变化,synchronized锁的是new--this,具体的对象(如一部部手机)
*static synchronized锁的是 静态 class---模板- 7、一个普通同步方法,一个静态同步方法,1部手机,请问先打印邮件还是短信
- sendSMS----------
- sendEmail-------
- 8、一个普通同步方法,一个静态同步方法,2部手机,请问先打印邮件还是短信
- sendSMS----------
- sendEmail-------
*/
重入锁:
重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞,并且支持获取锁时的公平和非公平性选择
该特性的实现需要解决以下两个问题。
1)线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
2)锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经成功释放
ReentrantLock:显式的重进入,调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。
synchronized:隐式的重进入,如一个synchronized修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁,而不像Mutex由于获取了锁,而在下一次获取锁时出现阻塞自己的情况。
公平锁与非公平锁:
非公平锁:线程饿死,但是效率高
公平锁:雨露均沾,效率相对较低
公平性与否是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO(先进先出)。
死锁:
之前的文章内容:Java多线程编程(同步、死锁、生产消费)
线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

产生死锁必须具备以下四个条件(操作系统部分):
- 互斥条件:该资源任意一个时刻只由一个线程占用。
- 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
- 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
解决方式:(破坏其中之一就好)
破坏互斥条件(无法破坏)
破坏请求与保持条件
破坏不剥夺条件
破坏循环等待条件
·避免一个线程同时获取多个锁。
·避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
·尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
·对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
实现代码:
package com.JUC;
import java.util.concurrent.TimeUnit;
public class deadlock05 {
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized(a){ //获取a资源,并加锁
System.out.println(Thread.currentThread().getName()+"持有a资源,试图获取b资源");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){ //获取b资源,并加锁
System.out.println(Thread.currentThread().getName()+"获取到b资源");
}
}
},"A线程").start();
new Thread(()->{
synchronized(b){ //获取b资源,并加锁
System.out.println(Thread.currentThread().getName()+"持有b资源,试图获取a资源");
synchronized (a){ //获取a资源,并加锁
System.out.println(Thread.currentThread().getName()+"获取到a资源");
}
}
},"B线程").start();
}
}
B线程持有b资源,试图获取a资源
A线程持有a资源,试图获取b资源
验证是否是死锁:
- jps 类似Linux ps -ef
- jstack JVM自带的堆栈跟踪工具
代码在运行的时候,进入命令行:

找到我们正在运行的程序pid.

观察下图:

最后显示发现一个死锁,然后上面的内容解析:
B线程当前锁的是....fb00,等待的是....faf0
A线程当前锁的是....faf0,等待的是....fb00;
JUC之多线程锁问题的更多相关文章
- Java并发编程(3) JUC中的锁
一 前言 前面已经说到JUC中的锁主要是基于AQS实现,而AQS(AQS的内部结构 .AQS的设计与实现)在前面已经简单介绍过了.今天记录下JUC包下的锁是怎么基于AQS上实现的 二 同步锁 同步锁不 ...
- Python多线程锁
[Python之旅]第六篇(四):Python多线程锁 python lock 多线程 多线程使用方法 多线程锁 摘要: 在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来 ...
- java 并发多线程 锁的分类概念介绍 多线程下篇(二)
接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...
- Java多线程--锁的优化
Java多线程--锁的优化 提高锁的性能 减少锁的持有时间 一个线程如果持有锁太长时间,其他线程就必须等待相应的时间,如果有多个线程都在等待该资源,整体性能必然下降.所有有必要减少单个线程持有锁的时间 ...
- synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解
本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁 ...
- Java - "JUC" ReentrantLock获取锁
[Java并发编程实战]-----“J.U.C”:ReentrantLock之一简介 ReentrantLock介绍 ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”. 顾名思义,R ...
- Java - "JUC" ReentrantLock释放锁
Java多线程系列--“JUC锁”04之 公平锁(二) 释放公平锁(基于JDK1.7.0_40) 1. unlock() unlock()在ReentrantLock.java中实现的,源码如下: p ...
- Java——多线程锁的那些事
引入 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率. 下面先带大家来总体预览一下锁的分类图 1.乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了 ...
- JUC(三):JUC包下锁概念
线程不安全集合类 ArrayList List是线程不安全的集合类,底层是Object数组实现,初始化容量是10(其实是一个空数组,第一次扩容时,将数组扩容为10),其后每次扩容大小为当前容量的一半( ...
随机推荐
- 【Python】【Basic】【数据类型】运算符与深浅拷贝
运算符 1.算数运算: 2.比较运算: 3.赋值运算: 4.逻辑运算: 5.成员运算: 三元运算 三元运算(三目运算),是对简单的条件语句的缩写. # 书写格式 result = 值1 if 条件 ...
- 3.0 go mod之远程仓库搭建-代码示例
注意事项 所谓的远程仓库指的是github,个人首次使用go mod在其他云仓库上尝试,并未成功,这浪费了我近2小时的时间: 如果你是初次尝试,那么除了github的地址换一下之外,其他的都按照示例操 ...
- 【Java】【IDE】【Jetbrain Idea】Intellij IDEA 快捷键整理
[常规] Ctrl+Shift + Enter,语句完成 "!",否定完成,输入表达式时按 "!"键 Ctrl+E,最近的文件 Ctrl+Shift+E,最近更 ...
- 关于ssh-keygen 生成的key以“BEGIN OPENSSH PRIVATE KEY”开头
现在使用命令 ssh-keygen -t rsa 生成ssh,默认是以新的格式生成,id_rsa的第一行变成了"BEGIN OPENSSH PRIVATE KEY" 而不在是&q ...
- spring的不同事务传播行为和用途。
1.PROPAGATION_REQUIRED:如果当前没有事务,就创建一个事务,如果当前存在事务,就加入该事务,该设置是最常用的设置. 2.PROPAGATION_SUPPORTS:支持当前事务,如果 ...
- 【C/C++】散列/算法笔记4.2
先说一下我自己的理解. 我先给你N组数据,这个N组里可能有重复的! 然后我们先统计好了N组里面的独立的每个对应的出现了几次(相当于map,然后每项属性有出现了多少次的),用的是数组下标对应 现在我们给 ...
- 【科研工具】流程图软件Visio Pro 2019 详细安装破解教程
[更新区] 安装教程我下周会在bilibili上传视频,这周事情太多暂时先不弄. [注意] 安装Visio需要和自己的Word版本一样,这里因为我的Word是学校的正版2019(所以学校为什么正版没买 ...
- JavaFile I/O流
Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基本类 ...
- ios消息队列APNS实现和证书申请
iOS消息推送的工作机制可以简单的用下图来概括: Provider是指某个iPhone软件的Push服务器,APNS是Apple Push Notification Service的缩写,是苹果的服务 ...
- Android CameraX ImageAnalysis 获取视频帧
CameraX使用ImageAnalysis分析器,可以访问缓冲区中的图像,获取视频帧数据. 准备工作 准备工作包括gradle,layout,动态申请相机权限,外部存储权限等等,大部分设置与Came ...