首页 > 编程语言 >【JavaEE】【多线程】synchronized和死锁

【JavaEE】【多线程】synchronized和死锁

时间:2024-10-14 15:17:42浏览次数:3  
标签:重入 加锁 synchronized JavaEE 死锁 线程 多线程 public

目录


一、synchronized详解

1.1 互斥

synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到
同一个对象 synchronized 就会阻塞等待.

语法:

synchronized(变量){
//修改操作
}

()括号内的变量不重要,作用是区分加锁对象是否一样,如果对同一个对象加锁,那么两个操作就会产生“blocked”锁竞争阻塞问题,后一个线程就会等到前一个线程解锁再执行。
进入左大括号 ‘{’ 就是加锁,出了右大括号 ‘}’ 就是解锁。

写一个例子,就会出现预期结果10000:

public class Demo7 {
    private static int ret;
    public static void main(String[] args) throws InterruptedException {
        Object block = new Object();
        
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {                
                synchronized (block){
                    ret++;
                }
                
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (block){
                    ret++;
                }

            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(ret);;
    }
}

synchronized还可以修饰方法(静态方法也行)。

  • synchronized修饰实例方法:
class Counter{
    public int ret;
    public void increase1() {
        synchronized (this) {
            ret++;
        }
    }
    //简化版本
    synchronized public void increase2() {
        ret++;
    }
}
  • synchronized修饰静态方法:相当于修饰这个类
class Counter{
	private static int ret2;
	public static void increase3() {
        synchronized (Counter.class) {
            ret2++;
        }
    }
    //简化版本
    synchronized public static void increase4() {
        ret2++;
    }
}

1.2 可重入

可重入就是指一个线程连续针对一个对象加多次锁,不会出现“死锁”现象称为可重入。

死锁:自己把自己锁死。
举个例子,假如synchronized不是可重入锁:

synchronized (block) {
	synchronized(block) {
	//代码
	} //右大括号}2
}  //右大括号}1

在进入第一个synchronized的时候,加上一把锁,此时已经是“锁定状态”,
当我们进入到第二个synchronized的时候要加锁,就发生“阻塞等待”,就要等到第一个锁走到右大括号}1解完锁才能加,
然而第一个锁走到右大括号}1解锁,又需要第二把锁创建走完到右大括号}2。
这是线程就卡死了,这就是死锁。

synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;

  • 可重入机制会在第一次加锁的时候记录是那个线程加的锁,然后下一次加锁如果是这个被记录线程,就直接加锁成功。这就是可重入锁的 "线程持有者"信息。
  • 可重入锁还会有一个“计数器”信息,加一把锁就加一,解一把锁就减一,当计数器递减为 0 的时候, 才真正释放锁。

二、死锁

  • 前面所说,一个线程两把锁,如果不是可重入锁就死锁了;
  • 还有两个线程两把锁,是不是可重入锁都要死锁。两个线程T1、T2,两把锁A、B;T1获取锁A,T2获取锁B,T2尝试获取锁B,T2尝试获取锁A。就如“车钥匙锁房间里了,房间钥匙锁车里了”。
public class Demo9 {
    public static void main(String[] args) {
        Object block1 = new Object();
        Object block2 = new Object();
        Thread thread1 = new Thread(()->{
            synchronized (block1) {
                System.out.println("thread1加block1");
                synchronized (block2) {
                    System.out.println("thread1加block2");
                }
            }
        });
        Thread thread2 = new Thread(()->{

            synchronized (block2) {
                System.out.println("thread2加block2");

                synchronized (block1) {
                    System.out.println("thread2加block1");
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

  • N个线程,M把锁(相当于两个线程两把锁的进阶版本):经典模型哲学家就餐问题

5个哲学家,5只筷子,哲学家坐在圆桌边,桌上放有面条,每只筷子放在每个哲学家的中间。

  1. 每个哲学家,会做两件事:
    1.1.思考人生.放下筷子,啥都不干
    1.2 吃面条.拿起左右两侧的两根筷子,开始吃面条,
  2. 哲学家啥时候吃面条,啥时候思考人生,是随机的
  3. 哲学家吃面条啥时候吃完,也是随机的,
  4. 哲学家正在吃面条的过程中,会持有左右两侧的筷子。此时相邻的哲学家如果也想吃面条,就需要阻塞等待,

当出现极端情况,每个哲学家都想吃面条,都拿起自己左手边的筷子,并且不会在没吃到面条情况下放下筷子,这时就是死锁了。

2.1 死锁成因

四个必要条件:

  1. 互斥使用(锁的基本特性):当一个线程拿到一把锁后,另一个线程要拿到这把锁就要阻塞等待;
  2. 不可抢占(锁的基本特性):当一把锁被线程拿到后,其他线程不能抢占,只能等线程自己释放锁;
  3. 请求保持(代码结构):当一个线程拿到一把锁后,再去拿其它锁的时候,已经被拿到的锁不会被释放;
  4. 循环/环路 等待(代码结构):阻塞等待的依赖关系形成环了。

2.2 避免死锁

只要上诉四个条件不成立一个就可以。
条件1,2是锁自带的特性,不能改变。

  • 对于条件3,改变代码结构,避免锁嵌套逻辑;
  • 对于条件4,可以约定加锁顺序,便面等待依赖关系成环。
    如上面的哲学家就餐问题,给每个筷子从1-5顺序编号,规定每次拿筷子只能先拿小号筷子。
  • 银行家算法,但是这种算法过于麻烦一般不用。

标签:重入,加锁,synchronized,JavaEE,死锁,线程,多线程,public
From: https://blog.csdn.net/yj20040627/article/details/142692263

相关文章

  • python中多线程和多进程的区别
    希望在1分钟内完成500架无人机的路径规划任务,而目前A*算法在50架无人机的情况下需要10秒,意味着在不做优化的情况下处理500架无人机将需要大约100秒,超出你的指标要求。提升计算速度是关键。多线程和多进程是常用的加速方案,但它们在Python中的效果存在差异1、多线程Python的标准......
  • day12-多线程
    day10-多线程一、多线程常用方法下面我们演示一下getName()、setName(Stringname)、currentThread()、sleep(longtime)这些方法的使用效果。publicclassMyThreadextendsThread{publicMyThread(Stringname){super(name);//1.执行父类Thread(Stringname......
  • day11-特殊文件、日志技术、多线程
    day11-特殊文件、日志技术、多线程一、属性文件1.1特殊文件概述同学们,前面我们学习了IO流,我们知道IO流是用来读、写文件中的数据。但是我们接触到的文件都是普通的文本文件,普通的文本文件里面的数据是没有任何格式规范的,用户可以随意编写,如下图所示。像这种普通的文本文件,没......
  • 【附源码】个人博客系统(源码+数据库+毕业论文齐全)java开发springboot框架vue javawe
    ......
  • 【JavaEE】【多线程】进程与线程的概念
    目录一、进程1.1系统管理进程1.2系统操作进程1.3进程控制块PCB关键属性1.4cpu对进程的操作1.5进程调度二、线程2.1线程与进程2.2线程资源分配2.3线程调度三、线程与进程区别四、线程简单操作代码4.1创建线程4.1.1start()与run()区别4.2查看线程一、进......
  • 【JavaEE】【多线程】Thread类讲解
    目录一、Thread构造方法二、Thread的常见属性三、创建一个线程四、获取当前线程引用五、终止一个线程5.1使用标志位5.2使用自带的标志位六、等待一个线程七、线程休眠八、线程状态九、线程安全9.1线程不安全原因总结9.2解决由先前线程不安全问题例子一、Thre......
  • java 网络知识 + 多线程问题
    服务器:packagep1007;importjava.io.*;importjava.net.*;importjava.util.Random;publicclassServer{publicstaticvoidmain(String[]args){intport=12345;//服务端口try(ServerSocketserverSocket=newServerSocket(port)......
  • JavaEE: 深入解析HTTP协议的奥秘(1)
    文章目录HTTPHTTP是什么HTTP协议抓包fiddle用法HTTP请求响应基本格式HTTPHTTP是什么HTTP全称为"超文本传输协议".HTTP不仅仅能传输文本,还能传输图片,传输音频文件,传输其他的各种数据.因此它广泛应用在日常开发的各种场景中.HTTP往往是基于传输层的......
  • gdb多线程多进程调试命令
    多线程infothreads查看当前所有运行线程的列表thread线程编号 切换到特定线程进行调试setscheduler-lockingon只运行当前线程,停止其他线程进行调试多进程infoinferions显示所有正在调试的进程inferion进程编号 切换到特定进程运行,同时挂起其他进程detach-on-fo......
  • Windows多线程编程 互斥量和临界区使用
    Windows多线程编程允许程序同时运行多个线程,提高程序的并发性和执行效率。多线程编程中的核心概念包括线程的创建、同步、调度、数据共享和竞争条件等。本文详细介绍了Windows多线程编程的关键技术点,并解释如何使用线程同步机制来保证线程安全。1.线程基础概念1.1线......