线程安全问题

一.定义:

一段代码,在多线程中,并发执行,出现bug的情况。

二.原因:

1)操作系统对于线程的调度是随机的,抢占式执行。

2)多个线程修改同一个变量。

3)线程的修改操作并不是原子的(也就是线程执行时调度了其他线程)。

4)内存可见性问题->编译器自动优化

5)指令重排序。

三.解决方案:

由于1是操作系统的特性,所以无法从这个方面来优化问题

1)加锁:关键字synchronized,就可以解决(3)类问题:

java 复制代码
Object locker1 = new Object();
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (locker) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

对于java来说,因为锁的可重性,在同一个线程加锁中再加锁,只要锁对象相同那么就可以:

java 复制代码
public static void main(String[] args) throws InterruptedException {
        Counter2 counter = new Counter2();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                synchronized (counter) {
                    synchronized (counter) {
                        synchronized (counter) {
                            counter.add();
                        }
                    }
                }
            }
        });
        t1.start();
        t1.join();

        System.out.println("count = " + counter.get());
    }

除此以外,synchronized还可以修饰方法,括号内要填入一个锁对象,可以是Object本身或者任意子类,那么加锁有好处,那么就会有一些新问题。

1.1)死锁:死锁会是程序卡住,从而无法往后执行

1.1.2)死锁形成原因:

1)锁与锁之间是互斥的,若想要使用同一个锁对象必须等待上一个锁 解锁并释放锁对象。

2)锁的不可剥夺\抢占的特点

3)锁的请求和保持:若锁处于阻塞状态,那么它就会一直等待和请求

java 复制代码
 public static void main(String[] args) throws InterruptedException {
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker2) {
                    System.out.println("t1 线程两个锁都获取到");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker2) {
                    System.out.println("t2 线程两个锁都获取到");
                }
            }

        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }

4)循环等待:多把锁需要多个锁对象,从而相互等待的情况。

1.1.3)死锁的解决办法:

1)避免锁嵌套,尽量使用一共锁对象=>打破3);

2)约定加锁顺序=>打破4);

2)volatile: 用此关键词来修饰变量那么就等于给编译器表明,此变量是"易失"的。

对于代码:

java 复制代码
package TreadDemo;

import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: czt20
 * Date: 2026 -05-16
 * Time: 15:36
 */
public class Demo20 {
   static int k = 0;
    public static void main(String[] args) throws InterruptedException {
       Object a = new Object();
       Object b = new Object();
       Thread t1 = new Thread(()->{
           synchronized (a){
               System.out.println("需要输入");
               while(k == 0){
                   try {
                       Thread.sleep(10000);
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
               System.out.println("t1线程被执行");
           }
       });
        Thread t2 = new Thread(()->{
            synchronized (b){
                Scanner scanner = new Scanner(System.in);
                k = scanner.nextInt();
                System.out.println("t2线程被执行");
            }
        });
        t1.start();
        t2.start();

    }
}

因为cpu在while循环中执行速度非常快,在我们输入之前已经执行了无数次,无数次k的结果都是0那么编译器自动将k从寄存器读取内存上的k值这一步省略,导致后续输入k的值,无济于事。那么volatile的修饰就是来阻止这种编译器优化的情况发生。

相关推荐
roman_日积跬步-终至千里16 分钟前
【数据治理(6)】DataOps 不是调度工具,而是让数据产品长期可信的运行机制
java·服务器·网络
来了就未晚17 分钟前
Python基础 学习代码存储与命名规范
开发语言·python·学习
落魄大学生之流水线上谋生计29 分钟前
LangGraph 基本用法指南
java·windows·python·langchain
布莱克60538 分钟前
数组详解:定义、作用、应用场景及与链表的区别(C/C++ 代码讲解)
c语言·开发语言·c++·数组
2601_9621284142 分钟前
SpringBoot篇(缓存层)
java·spring boot·缓存
hongmai6668881 小时前
MP2348GTL-Z:这颗24V/4A同步降压芯片,把性能和体积平衡得恰到好处
c语言·开发语言·arm开发·单片机·5g
多弗朗皮卡丘1 小时前
C语言梦开始的地方19:文件操作
c语言·开发语言
Dovis(誓平步青云)1 小时前
折叠屏悬停看视频,上半屏和下半屏应该各做什么
android·java·服务器·开发语言·安全·音视频
benchmark_cc1 小时前
Python量化实战:如何检测并剔除历史数据中的“闪崩/乌龙指”异常价格点?
开发语言·人工智能·python·量化·quantdash·量化数据源
2501_937860941 小时前
Java多线程初阶(下)—— synchronized、volatile、wait/notify与经典并发工具
java·开发语言·jvm