JAVA线程资源共享问题

JAVA多线程共享资源问题

场景引入

这个场景是一个典型的多线程共享资源的场景,主要目的是测试和观察多个线程对共享变量 sum 进行并发操作时是否会出现线程安全问题
场景描述

共享资源:

  • 共享变量 sum,初始值为 0。
  • 多个线程同时对 sum 进行操作,一个线程负责自增操作,另一个线程负责自减操作。

线程操作:

  • 每个线程在循环中对 sum 进行 1000 次操作。
  • 自增线程每次将 sum 增加 1,自减线程每次将 sum 减少 1。

日志记录:

  • 使用 Logger 记录每次操作的线程名称、操作类型(加法或减法)以及操作后的 sum 值。

目的

  • 测试线程共享问题:通过观察 sum 的值,测试是否存在线程安全问题。如果没有同步机制,可能会出现数据不一致的情况。
  • 验证并发操作的正确性:在没有同步机制的情况下,多个线程同时操作共享变量 sum,可能会导致数据竞争和不正确的结果。

预期结果

  • 线程安全问题:由于没有使用同步机制,多个线程同时操作 sum,可能会导致数据竞争,最终 sum 的值可能不正确。
    日志输出:日志会记录每次操作的详细信息,可以通过日志观察 sum 的变化情况。
public class syncThreadDemo01 extends Thread{

    Logger logger = LoggerFactory.getLogger(syncThreadDemo01.class);

    static Integer sum = 0;

    // 需求: 基于sum 开启多个线程,一个线程自增,一个线程减,测试是否有线程共享问题
    private Boolean isAdd;
    private String threadName;

    public syncThreadDemo01(Boolean isAdd,String threadName) {
        this.isAdd = isAdd;
        this.threadName = threadName;
    }

    @Override
    public void run() {
        for (int i = 0 ;i<1000;i++){
            if (isAdd){
                sum+=1;
            }else {
                sum-=1;
            }
            logger.info("线程:{}开始执行。。。。。。。。。。。,执行模式:{},输出结果:{}",threadName,isAdd?"加法":"减法",sum);
        }
    }
}
public class ThreadMainDemo {

    public static void main(String[] args) {
//        ThreadDemo thread = new ThreadDemo("线程1");
//        thread.start();

        // 测试 资源共享问题
        syncThreadDemo01 addThread = new syncThreadDemo01(true, "加线程");
        syncThreadDemo01 delThread = new syncThreadDemo01(false, "减线程");

        addThread.start();
        delThread.start();
    }
}

输出结果:
image

临界区

  • 一个程序运行多个线程本身是没有问题的
  • 问题出在多个线程访问共享资源
    • 多个线程读共享资源其实也没有问题
    • 在多个线程对共享资源读写操作时发生指令交错,就会出现问题
  • 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
    例如,下面代码中的临界区
    image

synchronized

为了避免临界区的竞态条件发生,有多种手段可以达到目的。

  • 阻塞式的解决方案:synchronized,Lock
  • 非阻塞式的解决方案:原子变量

本次使用阻塞式的解决方案:synchronized,来解决上述问题,即俗称的【对象锁】,它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换

image

输出结果:0

竟然说synchronizzed 是对象锁,那么我通过创建对象的方式,将对象作为形参会有什么效果,以下是静态对象和成员对象的方式
image

输出结果:不符合预期结果,产生了对资源共享的问题
分析:
由于创建线程的时候,分别创建了两次,所以在这个当中,对象锁锁的是它这个线程的本身,并没有对其他线程进行加锁。
image

竟然如此,换算成静态成员对象的方式,是否可以解决这个问题。
image
image

测试结果:符合预期,解决了多个线程 对 共享资源的问题。

变量的线程安全分析

成员变量和静态变量是否线程安全?

  • 如果它们没有共享,则线程安全
  • 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
    • 如果只有读操作,则线程安全
    • 如果有读写操作,则这段代码是临界区,需要考虑线程安全
posted @   自学Java笔记本  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示