Java 线程的同步与死锁
1、线程的同步产生的原因
没有同步的情况
class MyThread1 implements Runnable
{
private int ticket=5;
@Override
public void run() {
for(int x =0;x<20;x++)
{
if(ticket>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票:"+this.ticket--);
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
new Thread(mt,"票贩子A").start();
new Thread(mt,"票贩子B").start();
new Thread(mt,"票贩子C").start();
new Thread(mt,"票贩子D").start();
}
}
票贩子C卖票:5
票贩子D卖票:5
票贩子B卖票:4
票贩子A卖票:3
票贩子D卖票:2
票贩子C卖票:1
票贩子A卖票:-1
票贩子B卖票:0
票贩子D卖票:-2
可以看到又相同的票被卖了,还出现了负数票的情况。
2、线程的同步处理操作
实现同步的关键字synchronized,可以通过两种方式使用
- 一种是同步代码块
- 另外一种是同步方法
在Java里面有四种代码块:普通代码块、构造块、静态块、同步块
使用同步代码块实现同步
class MyThread1 implements Runnable
{
private int ticket=10;
@Override
public void run() {
for(int x =0;x<20;x++)
{
synchronized(this){
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"卖票:"+this.ticket--);
}
}
}
}
}
public class SynchronizedThread {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
new Thread(mt,"票贩子A").start();
new Thread(mt,"票贩子B").start();
new Thread(mt,"票贩子C").start();
new Thread(mt,"票贩子D").start();
}
}
票贩子A卖票:10
票贩子D卖票:9
票贩子C卖票:8
票贩子B卖票:7
票贩子C卖票:6
票贩子C卖票:5
票贩子C卖票:4
票贩子D卖票:3
票贩子A卖票:2
票贩子D卖票:1
调用同步方法实现同步
class MyThread1 implements Runnable
{
private int ticket=10;
@Override
public void run() {
for(int x =0;x<20;x++)
{
this.sale();
}
}
public synchronized void sale()
{
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"卖票:"+this.ticket--);
}
}
}
public class SynchronizedThread {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
new Thread(mt,"票贩子A").start();
new Thread(mt,"票贩子B").start();
new Thread(mt,"票贩子C").start();
new Thread(mt,"票贩子D").start();
}
}
3、线程的死锁情况
public class DeadLock {
public static void main(String[] args) {
DeadService deadService = new DeadService();
new Thread(new DeadRunnable(deadService),"a").start();
new Thread(new DeadRunnable(deadService),"b").start();
}
}
class DeadRunnable implements Runnable {
DeadService deadService;
public DeadRunnable(DeadService deadService) {
this.deadService = deadService;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
if ("a".equals(name)) {
deadService.methodA();
} else if ("b".equals(name)) {
deadService.methodB();
}
}
}
class DeadService {
final Object objectA = new Object();
final Object objectB = new Object();
/**
* 互斥条件:objectA、objectB对象只能同一时间被一个方法锁住
* 请求与保持条件:methodA()方法锁objectA对象的时候未释放又申请锁objectB对象,methodB()方法锁objectB对象的时候未释放又申请锁objectA对象
* 不可抢占条件: 锁objectA,锁objectB未释放前不能再被其他方法剥夺锁
* 循环等待条件:methodA()等待objectB对象锁的释放、methodB()等待objectA对象锁的释放
*/
public void methodA() {
synchronized (objectA) {
System.out.println("methodA() start.." + Thread.currentThread().getName());
ThreadUtil.sleep(3000);
synchronized (objectB) {
System.out.println("methodA() end.." + Thread.currentThread().getName());
}
}
}
public void methodB() {
synchronized (objectB) {
System.out.println("methodB() start.." + Thread.currentThread().getName());
ThreadUtil.sleep(3000);
synchronized (objectA) {
System.out.println("methodB() end.." + Thread.currentThread().getName());
}
}
}
}
运行结果:
methodA() start..a
methodB() start..b
发现程序卡在这里,系统出现死锁。
死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
java 死锁产生的四个必要条件:
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
解决方法:
- 用信号量去控制死锁。
- 调整申请锁的范围
- 调整申请锁的顺序
避免死锁:银行家算法。
排查死锁的方式:
- Java 进程的信息
E:\code\IdeaProjects\learn-java>jps
8096 Jps
13060
18868 DeadLock
17500 Launcher
jps是 Java Virtual Machine Process Status Tool(Java 虚拟机进程状态工具)的缩写。它是 JDK 提供的一个命令行工具,用于列出当前系统上正在运行的 Java 进程的信息,包括 Java 进程的进程 ID(PID)和主类的名称
- 打印死锁进程的堆栈信息
E:\code\IdeaProjects\learn-java>jstack -l 18868
……
……
……
Java stack information for the threads listed above:
===================================================
"a":
at com.zjw._02.synstatic.DeadService.methodA(DeadLock.java:71)
- waiting to lock <0x0000000622ed3648> (a java.lang.Object)
- locked <0x0000000622ed3638> (a java.lang.Object)
at com.zjw._02.synstatic.DeadRunnable.run(DeadLock.java:48)
at java.lang.Thread.run(java.base@17.0.10/Thread.java:840)
at java.lang.Thread.run(java.base@17.0.10/Thread.java:840)
"b":
at com.zjw._02.synstatic.DeadService.methodB(DeadLock.java:81)
- waiting to lock <0x0000000622ed3638> (a java.lang.Object)
- locked <0x0000000622ed3648> (a java.lang.Object)
at com.zjw._02.synstatic.DeadRunnable.run(DeadLock.java:50)
at java.lang.Thread.run(java.base@17.0.10/Thread.java:840)
Found 1 deadlock.
通过堆栈信息我们可以查看死锁发生的位置。
请解释多个线程访问统一资源时需要考虑哪些情况?有可能带来哪些后果?
多个线程访问同一资源时要考虑到线程间的同步问题,可以使用同步代码或同步方法解决;
同步代码块:synchronized(锁定对象){代码}
同步方法: public synchronized 返回值 方法名称(){代码}
但是过多的使用同步,有可能造成死锁。
Java 线程的同步与死锁的更多相关文章
- 菜鸡的Java笔记 - java 线程的同步与死锁 (同步 synchronization,死锁 deadlock)
线程的同步与死锁 (同步 synchronization,死锁 deadlock) 多线程的操作方法 1.线程同步的产生与解决 2.死锁的问题 ...
- 进阶Java编程(3)线程的同步与死锁
线程的同步与死锁 1,同步问题引出 在多线程的处理之中,可以利用Runnable描述多个线程操作的资源,而Thread描述每一个线程对象,对于当多个线程访问统一资源的时候如果处理不当就会产生数据的错误 ...
- Java多线程编程(同步、死锁、生产消费者问题)
Java多线程编程(同步.死锁.生产消费): 关于线程同步以及死锁问题: 线程同步概念:是指若干个线程对象并行进行资源的访问时实现的资源处理保护操作: 线程死锁概念:是指两个线程都在等待对方先完成,造 ...
- java 线程数据同步
java 线程数据同步 由买票实例 //java线程实例 //线程数据同步 //卖票问题 //避免重复卖票 //线程 class xc1 implements Runnable{ //定义为静态,可以 ...
- Java 线程与同步的性能优化
本文探讨的主题是,如何挖掘出Java线程和同步设施的最大性能. 1.线程池与ThreadPoolExecutor 1)线程池与ThreadPoolExecutor 线程池的实现可能有所不同,但基本概念 ...
- java多线程(同步与死锁问题,生产者与消费者问题)
首先我们来看同步与死锁问题: 所谓死锁,就是A拥有banana.B拥有apple. A对B说:你把apple给我.我就把banana给你. B对A说:你把banana给我,我就把apple给你. 可是 ...
- java多线程(同步和死锁,生产者和消费者问题)
首先我们来看看同步与死锁: 所谓死锁.这是A有banana,B有apple. A至B说:你把apple对我来说,,我会banana给你. B至A说:你把banana对我来说,,我会apple给你. 可 ...
- Java线程:同步
一 同步的概念 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. MyRunnab ...
- java 线程(六)死锁
package cn.sasa.demo4; public class ThreadDemo { public static void main(String[] args){ DeadLockRun ...
- Java线程安全同步容器
线程安全同步容器(使用 synchronized关键字) 1.ArrayList->Vector,Stack 2.HashMap->HashTable(key.value不能为null) ...
随机推荐
- Typora Emoji图标
转自: https://www.cnblogs.com/wangjs-jacky/p/12011208.html People :smile: :laughing: :blush: :sm ...
- 你还不会使用Pycham Remote development 打开远程主机工作目录吗?这篇文章帮你解决!
前言 必备: 本地开发机与远程主机都要安装Pycharm专业版!!! 废话不多说直接开始!! 1.打开pycharm 2.依次点击File.Remote Development 3.依次点击SSH.N ...
- rgba颜色转换为十六进制
RGBA颜色转HEX 转换步骤: 先将r.g.b分别转换为十六进制,比如 r.g.b分别为 255,则转换后得到的为 FF.FF.FF 将a 乘以 255,然后获得的积的整数部分转换为十六进制,如 a ...
- Kubernetes - [04] 常用命令
kubectl 语法 kubectl [command] [TYPE] [NAME] [flags] command:指定在一个或多个资源商要执行的操作.例如:create.get.describe. ...
- 纯离线部署本地知识库LLM大模型
纯离线部署本地知识库LLM大模型 一.下载离线大模型 下载的网址:https://hf-mirror.com/ deepseek qwen 相关的模型,只建议使用1.5B的,GGUF后缀的模型 推荐下 ...
- kafka 认识kafka(一)
一.简介 1.1 概述 Kafka是最初由Linkedin公司开发,是一个分布式.分区的.多副本的.多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/ ...
- 大数据之路Week10_day07 (JavaAPI 操作Redis 与Hbase建立索引,通过查询redis中的索引查询Hbase数据)
在这里是简单模拟将索引存到redis中,再通过先查询索引再将Hbase中的数据查询出来. 需要考虑的问题: 1.建立redis的连接,建立Hbase的连接 2.如何创建索引,即创建索引的key和val ...
- PVE 配置显卡直通
博客链接:PVE 配置显卡直通 配置 Device: Dell PowerEdge T630 CPU: Intel(R) Xeon(R) E5-2696 v4 x2 GPU 1: Matrox Ele ...
- swoole(7)php进程间通信-消息队列
php实现消息队列操作 ftok:可以将一个路径转换成消息队列可用的key值 msg_get_queue:第一个参数是消息队列的key 第二个参数是消息队列的读写权限 server代码: <?p ...
- [tldr]GO使用正则表达式
简述如何使用GO调用正则表达式 是否符合条件 使用MatchString方法实现 _, err := regexp.MatchString(regex, str) 提取内容 Compile 第一步需要 ...