面试必问的线程安全问题

本文讨论了线程不安全的原因,包括线程抢占式执行、并发操作同一变量、非原子性操作以及内存可见性问题。通过实例分析了线程安全问题,并介绍了使用`synchronized`关键字保护线程安全的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

感谢

观迎各位网友能抽出宝贵时间来看我的博客,也欢迎各大网友指正我的不足之处!

好了。我们来看一下面试经常会问的线程安全问题。

首先,为什么线程会不安全,意思是,线程不安全的几个原因。我来总结一下。

第一,线程是抢占式执行,线程之间的调度之间充满不确定性,和随机性,这是计算机系统本身来设计的,我们不能改变。(这就是根本原因)。

第二,多个线程对同一变量及进行操作,此处指的操作是(多个线程对同一变量读和写),如果多个变量同时对同一变量都没事,对不同变量写没事。

第三点,针对变量的操作不是原子性的,就是说一个操作还可以分成好几小部分来执行,这个容易出错。

针对前面三点,我们来举一个例子。

让两个线程针对同一变量count=0,自增5000次,你们项的结果可能是10000.但是其实不是的。

沃尔玛能写出代码来展示一下。

public class TestDemo6 {
    static int count=0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
                count++;
            }
        });
        Thread thread2=new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
                count++;
            }
        });
        //开始执行线程
        thread1.start();thread2.start();
        //等待线程执行结束
        thread1.join();thread2.join();
        //打印结果
        System.out.println(count);
    }
}

执行了三次结果分别是:

but why,为啥呢。原因就在于上面说的三条。

不要小瞧这个count++;他是不满足原子性的,就是说count++,还可以分成3部分操作,来执行。

 

 

如上图这么执行操作并没有什么问题,但是我说过线程会发生抢占式执行,所以会出现以下的现象,结果会小于10000;

 

 

 来看视频讲解。

线程安全问题举例讲解

第四种就是内存可见性,这个名字有点抽象,就是JVM为了优化代码,自行操作,提高运行速度。

比如来举一个例子

public class TestDemo6 {
    //=内存可见性
    static int isquit=0;
    public static void main(String[] args) {
        Thread thread=new Thread(){
            @Override
            public void run() {
                while(isquit==0){
                    ;
                }
                System.out.println("我是一个线程");
            }
        };
        thread.start();
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入一个不是0的数来让线程结束");
        isquit=scanner.nextInt();
        System.out.println("main执行结束");
    }

}

结果是;

但是为啥子进程还在运行呢,

 所以我们刚才修改了数值以后,子进程并不会因为我们的修改而停止,我们可以判断,这个代码直接从CPU中的寄存器来读取数值,而不是从内存。

还有一种就是,指令重排序!

所以为了防止这种情况,我们还是有办法解决这中问题的。

怎么保护线程的安全

举例演示 

 static int count=0;
    synchronized public static void func(){
        count++;
    }

    public static void main(String[] args) throws InterruptedException {

        Thread thread1=new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
                func();
            }
        });
        Thread thread2=new Thread(()-> {
            for (int i = 0; i < 5000; i++) {
                func();
            }
        });

        thread1.start();thread2.start();

        thread1.join();thread2.join();

        System.out.println(count);
    }
}

我们给这个func方法加锁,使用synchronaized,关键字来修饰,保证每次只能一个线程来使用。在每个count++;前面加锁,一个线程正在执行,那么另一个线程,因为正在执行的线程有锁,那就不能抢占了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值