在多线程编程中,要执行synchronized块,必须首先获得指定对象的锁。

1.Java的线程锁是可重入的锁

    public void add(int m){
synchronized (lock){
this.value += m;
}
}

什么叫可重入的锁?

对同一个对象,同一个线程,可以多次获取它的锁,即同一把锁可以嵌套。

在add方法中,用synchronized获取了一个lock对象的锁,在synchronized代码块内部,调用another方法,可以再次获取到lock对象的锁。因为这是同一个线程对lock对象获取锁,所以它是一种可重入的锁。

    public void add(int m){
synchronized (lock){
this.value += m;
another(m);
}
}
public void addAnother(int m){
synchronized (lock){
this.another += m;
}
}
//上述代码可以合并为
public void add(int m){
synchronized (lock){
this.value += m;
synchronized (lock){
this.another += m;
}
}
}

2. Java的线程还可以获取多个不同对象的锁

先用synchronized(lockA)获得lockA的锁,在用synchronized(lockB)获得lockB的锁。在释放锁的时候,会依次释放lockB的锁,再释放lockA的锁。

    public void add(int m){
synchronized (lockA){//获取lockA的锁
this.value += m;
synchronized (LockB){//获取lockB的锁
this.another += m;
}//释放lockB的锁
}//释放lockA的锁
}

3.死锁

不同的线程获取多个不同对象的锁可能导致死锁

线程1和线程2分别执行。

线程1获取lockA的锁,线程2获得lockB的锁。

线程1执行语句,线程2执行语句。

线程1等待lockB的锁,线程2等待lockA的锁。这个时候,线程1和线程2会永远等待下去,谁也无法继续执行,就形成了死锁。



死锁形成的条件:

  • 两个线程各自持有不同的锁。线程1持有A锁,线程2持有B锁。
  • 两个线程各自试图获取对方的锁。线程1等待B锁,线程2等待A锁。
  • 双方无限等待下去,导致死锁

死锁发生后:

  • 没有任何机制能解除死锁
  • 只能强制结束JVM进程

所以,我们在编写多线程程序时,必须要避免出现死锁。

如何避免死锁:线程获取锁的顺序要一致

4.示例

4.1 死锁

class ShareObject{
final Object lockA = new Object();
final Object lockB = new Object();
int accountA = 1000;
int accountB = 2000;
public void a2b(int balance){ //a2b: acountA减, accountB加
synchronized (lockA){
accountA -= balance;
synchronized (lockB){
accountB += balance;
}
}
}
public void b2a(int balance){ //b2a:acountA加, accountB减
synchronized (lockB){
accountB -= balance;
synchronized (lockA){
accountA += balance;
}
}
}
}
class AThread extends Thread{
public void run(){
for(int i=0;i<Main.LOOP;i++){
Main.shared.a2b(1);
if(i%100==0){
System.out.println(".");
}
}
}
}
class BThread extends Thread{
public void run(){
for(int i=0;i<Main.LOOP;i++){
Main.shared.b2a(1);
if(i%100==0){
System.out.println(".");
}
}
}
}
public class Main{
final static int LOOP = 1000;
public static ShareObject shared = new ShareObject();
public static void main(String[] args) throws Exception{
Thread t1 = new AThread();
Thread t2 = new BThread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("end");
}
}


### 4.2 避免死锁
修改b2a()
```#java
public void b2a(int balance){
synchronized (lockA){
accountA += balance;
synchronized (lockB){
accountB -= balance;
}
}
}
}
```

4总结

  • 死锁产生的条件:

    * 多线程各自持有不同的锁,并互相试图获取对方已持有的锁
  • 如何避免死锁?

    * 多线程获得锁的顺序要一致

廖雪峰Java11多线程编程-2线程同步-3死锁的更多相关文章

  1. 廖雪峰Java11多线程编程-2线程同步-2synchronized方法

    1.Java使用synchronized对一个方法进行加锁 class Counter{ int count = 0; public synchronized void add(int n){ cou ...

  2. 廖雪峰Java11多线程编程-2线程同步-1同步代码块

    1.线程安全问题 多个线程同时运行,线程调度由操作系统决定,程序本身无法决定 如果多个线程同时读写共享变量,就可能出现问题 class AddThread extends Thread{ public ...

  3. 廖雪峰Java11多线程编程-2线程同步-4wait和notify

    wait和notify synchronized解决了多线程竞争的问题 我们可以在synchronized块中安全的对一个变量进行修改,但是它没有解决多线程协调的问题. 例如设计一个TaskQueue ...

  4. 廖雪峰Java11多线程编程-1线程的概念-1多线程简介

    多任务 现代操作系统(windows,MacOS,Linux)都可以执行多任务: 多任务就是同时运行多个任务,例如同时开启钉钉.百度网盘.火狐.谷歌.ps等 操作系统执行多任务就是让多个任务交替执行, ...

  5. 廖雪峰Java11多线程编程-4线程工具类-1ThreadLocal

    多线程是Java实现多任务的基础: Thread ExecutorService ScheduledThreadPool Fork/Join Thread对象代表一个线程:调用Tread.curren ...

  6. 廖雪峰Java11多线程编程-1线程的概念-2创建新线程

    Java语言内置多线程支持: 一个Java程序实际上是一个JVM进程 JVM用一个主线程来执行main()方法 在main()方法中又可以启动多个线程 1.创建新线程 1.1 方法一:使用Thread ...

  7. 廖雪峰Java11多线程编程-1线程的概念-5中断线程

    1.中断线程: 如果线程需要执行一个长时间任务,就可能需要中断线程.场景:从网络上下载一个100M的文件,用户在下载过程中中断下载任务的执行. 中断线程就是其他线程给该线程发一个信号,该线程收到信号后 ...

  8. 廖雪峰Java11多线程编程-1线程的概念-3线程的状态

    1线程的状态 线程终止的的原因: run()或call()方法执行完成,线程正常结束 线程抛出一个未捕获的Exception或Error 直接调用该线程的stop()方法来结束该线程--该方法容易导致 ...

  9. 廖雪峰Java11多线程编程-3高级concurrent包-4Concurrent集合

    Concurrent 用ReentrantLock+Condition实现Blocking Queue. Blocking Queue:当一个线程调用getTask()时,该方法内部可能让给线程进入等 ...

随机推荐

  1. 剑指offer——11矩阵覆盖

    题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?   题解: 使用递归或者动态规划,明显,递归没有动态规划优 ...

  2. 绿色版mysql 免安装使用(转载)

    MySQL绿色版的安装(mysql-5.6.22-win32.zip) Posted on 2015-01-31 23:21 卒子 阅读(10739) 评论(2) 编辑 收藏 由于工作需要最近要开始研 ...

  3. 28-Ubuntu-远程管理命令-02-查看网卡的配置信息

    命令 功能 ifconfig 查看网卡配置信息 ifconfig | grep inet 查看网卡对应的IP地址 ping  127.0.0.1 检测本地网卡是否正常 ping  IP地址 检测到目标 ...

  4. 【POJ】1502 MPI Maelstrom

    题目链接:http://poj.org/problem?id=1502 题意:一个处理器给n-1个处理器发送广播,问最短时间.广播时并发,也就是各个路径就大的一方.输入如果是x的话说明两个处理器不能相 ...

  5. (一)hello word

    1.在如图demo文件夹下可以编写自己的后端代码. 我们编写hello的接口代码如下: @RestController @RequestMapping("/hello") @Slf ...

  6. Jmeter-----请求依赖之JsonExtractor

    层级关系填写: 1.第一个必须是$ 2.用英文状态下的  . 来代表下一个层级

  7. asp.net core2.0 依赖注入 AddTransient与AddScoped的区别 - 晓剑 - CSDN博客

    原文:asp.net core2.0 依赖注入 AddTransient与AddScoped的区别 - 晓剑 - CSDN博客 原文地址:http://www.tnblog.net/aojiancc2 ...

  8. iOS开发系列-UIImageView的contentMode

    typedef NS_ENUM(NSInteger, UIViewContentMode) { UIViewContentModeScaleToFill, UIViewContentModeScale ...

  9. Hive中SQL查询转换成MapReduce作业的过程

  10. python ORM框架:SqlAlchemy

    ORM,对象关系映射,即Object Relational Mapping的简称,通过ORM框架将编程语言中的对象模型与数据库的关系模型建立映射关系,这样做的目的:简化sql语言操作数据库的繁琐过程( ...