线程安全问题

一.定义:

一段代码,在多线程中,并发执行,出现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的修饰就是来阻止这种编译器优化的情况发生。

相关推荐
旖旎夜光3 分钟前
C++(内存管理)
开发语言·c++·学习
whn19776 分钟前
达梦连接串JDBC测试程序
java·数据库
还是鼠鼠10 分钟前
Spring AI ChatClient详解:创建第一个AI客户端
java·springboot·spring ai·chatclient
≮傷£≯√15 分钟前
QT配置FFmpeg
开发语言·qt·ffmpeg
vHelios26 分钟前
【电商项目】测试 授权功能 遇到的问题与解决方案
java·spring boot·sql
三84427 分钟前
RCE长度&字符限制绕过
开发语言·php
小陈的进阶之路1 小时前
爬虫三大解析库:lxml, jsonpath, bs4
开发语言·笔记·python
wuyk5551 小时前
第1章:无刷电机核心原理与运行机制
c语言·开发语言·stm32·单片机·嵌入式硬件·机器学习
2401_894915531 小时前
GEO 定位优化源码搭建常见报错排查:数据库、伪静态、接口调试
java·数据库·网络协议·tcp/ip·spring·unity
SomeB1oody1 小时前
【RustyML入门】2.10. 主成分分析
开发语言·后端·机器学习·rust·教程