线程安全问题

一.定义:

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

相关推荐
likerhood1 小时前
设计模式-装饰器模式(java)
java·设计模式·装饰器模式
爱学习的小可爱卢1 小时前
Java抽象类与接口:面试高频考点全解析
java·javase
techdashen1 小时前
Rust 模块和文件不是一回事:一次讲清 `mod`、`use`、`pub use`
开发语言·后端·rust
Wy_编程1 小时前
go中的协程Goroutine
开发语言·golang
2401_867623982 小时前
如何管理应用锁_DBMS_LOCK申请自定义锁控制并发逻辑
jvm·数据库·python
basketball6162 小时前
C++ 命名空间知识点总结:从入门到合理设计
开发语言·c++
WL_Aurora2 小时前
Java多线程详解(一)
java·开发语言
woxihuan1234562 小时前
SQL数据分析如何剔除极端异常值_配合窗口函数检测偏离度
jvm·数据库·python
2303_821287382 小时前
Go 中通过指针实现变量名的“间接引用”与原地修改
jvm·数据库·python