首页 > 编程语言 >Java之多线程的同步和死锁

Java之多线程的同步和死锁

时间:2023-07-11 21:57:47浏览次数:35  
标签:Java Thread s2 s1 instance 死锁 new 多线程 public

设计模式中的单例模式的懒汉方式会存在多线程的安全问题;通过以下测试代码可以看到两个线程中得到的并不是同一个单例对象;

    @Test
    public void unsafeSingleInstanceTest() throws InterruptedException {
        AtomicReference<UnSafeSingleInstance> s1 = new AtomicReference<>();
        AtomicReference<UnSafeSingleInstance> s2 = new AtomicReference<>();
        var t1 = new Thread(()->{
            s1.set(UnSafeSingleInstance.getInstance());
        });

        var t2 = new Thread(()->{
            s2.set(UnSafeSingleInstance.getInstance());
        });

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

        System.out.println("(s1 == s2) = " + (s1.get() == s2.get()));
        System.out.println("s1 = " + s1.get());
        System.out.println("s2 = " + s2.get());

//        (s1 == s2) = false
//        s1 = UnSafeSingleInstance@43a25848
//        s2 = UnSafeSingleInstance@3ac3fd8b
    }
    
    
class UnSafeSingleInstance{
    private static UnSafeSingleInstance instance = null;

    private UnSafeSingleInstance(){}

    public static UnSafeSingleInstance getInstance()  {
        if(instance == null){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            instance = new UnSafeSingleInstance();
        }

        return instance;
    }
}

我们可以通过在getInstance方法中使用synchronized添加同步代码块,同时进行双重的null检查避免无谓的锁占用;

    @Test
    public void safeSingleInstanceTest() throws InterruptedException {
        AtomicReference<SafeSingleInstance> s1 = new AtomicReference<>();
        AtomicReference<SafeSingleInstance> s2 = new AtomicReference<>();
        var t1 = new Thread(()->{
            s1.set(SafeSingleInstance.getInstance());
        });

        var t2 = new Thread(()->{
            s2.set(SafeSingleInstance.getInstance());
        });

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

        System.out.println("(s1 == s2) = " + (s1.get() == s2.get()));
        System.out.println("s1 = " + s1.get());
        System.out.println("s2 = " + s2.get());

//        (s1 == s2) = true
//        s1 = SafeSingleInstance@2133c8f8
//        s2 = SafeSingleInstance@2133c8f8
    }
    
class SafeSingleInstance{
    private static SafeSingleInstance instance = null;

    private SafeSingleInstance(){}

    public static SafeSingleInstance getInstance()  {
        if(instance == null){
            synchronized (SafeSingleInstance.class){
                if(instance == null){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    instance = new SafeSingleInstance();
                }
            }
        }

        return instance;
    }
}

除了使用synchronized之外,也可以使用JUC提供的Lock来实现线程的同步;

    @Test
    public void safeSingleInstanceWithLockTest() throws InterruptedException {
        AtomicReference<SafeSingleInstanceWithLock> s1 = new AtomicReference<>();
        AtomicReference<SafeSingleInstanceWithLock> s2 = new AtomicReference<>();
        var t1 = new Thread(()->{
            s1.set(SafeSingleInstanceWithLock.getInstance());
        });

        var t2 = new Thread(()->{
            s2.set(SafeSingleInstanceWithLock.getInstance());
        });

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

        System.out.println("(s1 == s2) = " + (s1.get() == s2.get()));
        System.out.println("s1 = " + s1.get());
        System.out.println("s2 = " + s2.get());

//        (s1 == s2) = true
//        s1 = SafeSingleInstanceWithLock@43a25848
//        s2 = SafeSingleInstanceWithLock@43a25848
    }
    
class SafeSingleInstanceWithLock{
    private static volatile SafeSingleInstanceWithLock instance = null;

    private static Lock lock = new ReentrantLock();

    private SafeSingleInstanceWithLock(){}

    public static SafeSingleInstanceWithLock getInstance()  {
        if(instance == null){
            lock.lock();
            if(instance == null){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                instance = new SafeSingleInstanceWithLock();
            }
            lock.unlock();
        }

        return instance;
    }
}

多线程的同步可能会出现死锁;死锁的出现是由于发生死锁的两个线程持有彼此需要的锁资源,导致双方都无法执行只能等待;
以下测试代码以构成一个商品的两个组件为例,组装商品我们可以从APart开始,也可以从BPart开始;

    @Test
    public void deadThreadTest() throws InterruptedException {
        APart a = new APart();
        BPart b = new BPart();
        var aPartWorker =new Thread(){
            @Override
            public void run() {
                a.doWork(b);
            }
        };

        var bpartWorker =new Thread(){
            @Override
            public void run() {
                b.doWork(a);
            }
        };

        aPartWorker.start();
        bpartWorker.start();
        aPartWorker.join();
        bpartWorker.join();

//        APart do self step.
//        BPart do self step.
//        waiting a.doSelfStep.
//        waiting b.doSelfStep.
    }
    
class APart{
    public synchronized  void doWork(BPart b) {
        doSelfStep();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("waiting b.doSelfStep.");
        b.doSelfStep();
    }

    public synchronized  void doSelfStep(){
        System.out.println(this.getClass().getName() + " do self step.");
    }
}

class BPart{

    public synchronized  void doWork(APart a)  {
        doSelfStep();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("waiting a.doSelfStep.");
        a.doSelfStep();
    }

    public synchronized  void doSelfStep(){
        System.out.println(this.getClass().getName() + " do self step.");
    }
}

标签:Java,Thread,s2,s1,instance,死锁,new,多线程,public
From: https://www.cnblogs.com/wufengtinghai/p/17546021.html

相关文章

  • Java 中的值传递和引用传递 ?
    一.介绍值传递:值传递(Pass-by-Value)当我们向方法传递参数时,实际上是将该参数的值进行拷贝,并将拷贝后的值传递给方法内部。在方法内部对参数进行修改不会影响原始变量的值。引用传递:引用传递(Passbyreference)是指方法调用时实参(即传入方法的参数)是一个对象的引用,而不是......
  • Java学习day01
    我在B站上大学......
  • java中指定范围随机数的产生
    1、Random()方法Random()方法需要引入java.util.Random类,此随机数种子是以当前时间为默认种子,需要注意的是0<=random.nextInt(100)<100。importjava.util.Random;publicclassMain{publicstaticvoidmain(String[]args){Randomrandom=newRandom();......
  • 基于java+springboot的宠物商店、宠物管理系统
    该系统是基于java+springboot开发的宠物商城,用户可以登录该网站购买宠物。该系统是给师弟开发的课程作业。运行过程中的问题,可以咨询github或留言。演示地址前台地址:http://pet.gitapp.cn后台地址:http://pet.gitapp.cn/admin后台管理帐号:用户名:admin123密码:admin123源......
  • 基于java+springboot的旅游信息网站、旅游景区门票管理系统
    该系统是基于java+springboot开发的旅游景区门票管理系统。是给师弟开发的大四实习作品。学习过程中,遇到问题可以咨询github作者。演示地址前台地址:http://travel.gitapp.cn后台地址:http://travel.gitapp.cn/admin后台管理帐号:用户名:admin123密码:admin123源码地址htt......
  • 基于java+springboot的家教预约网站、家教信息管理系统
    该系统是基于java+springboot开发的家教预约网站。是给师妹开发的实习作品。学习过程中,遇到问题可以在github咨询作者。演示地址前台地址:http://jiajiao.gitapp.cn后台地址:http://jiajiao.gitapp.cn/admin后台管理帐号:用户名:admin123密码:admin123源码地址https://git......
  • 老杜 JavaWeb 讲解(九) ——模板方法设计模式、HttpServlet源码分析
    (十一)模板方法设计模式、HttpServlet源码分析对应视频:20-HttpServlet源码分析及web欢迎页11.1模板方法设计模式不用使用在上面右侧表格中,Person就是模板方法设计模式当中的模板类,通常是抽象类。day()方法就是模板方法设计模式当中的模板方法。模......
  • 基于java+springboot的求职招聘网站-求职招聘管理系统
    该系统是基于java+springboot开发的求职招聘网站、网上招聘管理系统、网上人才招聘系统、毕业生求职招聘系统、大学生求职招聘系统、校园招聘系统、企业招聘系统。是给师弟开发的毕业设计。大家学习过程中,遇到问题可以咨询作者vx(lengqin1024)演示地址前台地址:http://job.gitap......
  • 基于java+springboot的视频点播网站-在线视频点播系统
    该系统是基于java+springboot开发的视频点播系统。是给师妹开发的毕业设计。演示地址前台地址:http://video.gitapp.cn后台地址:http://video.gitapp.cn/admin后台管理帐号:用户名:admin123密码:admin123源码地址https://github.com/geeeeeeeek/java_video功能介绍平台......
  • 基于java+springboot的外卖点餐网站、外卖点餐管理系统
    该系统是基于java+springboot开发的外卖点餐网站、外卖点餐管理系统。是给师弟开发的课程作业。运行过程中的问题,可以在github咨询作者。演示地址前台地址:http://food.gitapp.cn后台地址:http://food.gitapp.cn/admin后台管理帐号:用户名:admin123密码:admin123源码地址h......