• 【java】锁对象状态的改变会导致非线程安全


    问题描述

    使用synchronized在一个非final对象上加了锁之后,在synchronized体(同步代码块)中,将该对象的值(状态)改变之后,会导致线程不安全,即其他线程会拿到改变之后对象的锁,从而进入同步代码块。

    场景设计

    public class TestLock extends Thread{
        private AAA aaa;
         
        public TestLock(AAA aaa) {
            this.aaa = aaa;
        }
     
        @Override
        public void run() {
            this.aaa.lockMethod();
        }
     
        public static void main(String[] args) {
            AAA aaa = new AAA();
            Thread a1 = new TestLock(aaa);
            Thread a2 = new TestLock(aaa);
            a1.start();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            a2.start();
        }
    }
     
    class AAA{
        private Boolean lock = true;
     
        public void lockMethod() {
            synchronized (lock) {
                lock = !lock;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(lock);
            }
        }
    }

    场景说明

    两个线程a1,a2,访问同一个AAA对象aaa(临界资源),AAA中的lockMethod方法中是一个同步代码块,锁加在非final的lock对象上,在同步代码块中改变lock对象的值。main函数中500ms的休眠是能够让线程a1先获得lock的锁,线程a1先访问同步代码块,将lock的值变成false,接着休眠一秒。

    假设

    如果lock值的改变不会影响线程安全的特性,那么线程a2拿不到lock的锁,在休眠结束之后,应该输出false,然后,线程a2拿到lock锁,进入同步代码块,将lock的值变成true,然后休眠一秒,休眠结束后,输出lock的值true,即输出的两个值是false和true。

    程序运行结果

    true和true

    程序结果分析

    第一个的输出结果是true,说明,线程a1进入同步代码块之后,将lock的值变成false,进入休眠,线程a2能够拿到修改后的lock对象的锁,进入同步代码块,将lock的值变成true,然后线程a2进入休眠,线程a1休眠结束之后,输出被线程a2修改之后的lock值true,然后,线程a2休眠结束之后,同样输出true,符合程序输出结果。

    总结与注意

    对于非对象上的锁,在同步代码块中修改起状态后,会导致线程不安全,即其他线程会拿到修改后的对象上的锁。

    对于需要加锁的对象,可以设置成final,这样就避免了对其的修改,如果是非final的对象,需要做到在同步块中避免对锁对象的修改。

  • 相关阅读:
    特殊的空格-ASCII码值160
    动态行转列且一行转多列
    SQL事务
    String.Join 方法
    jQuery multiselect初始化默认值及多选项保存到数据库
    .net使用 SmtpClient 发邮件
    养气
    springboot后台解决跨域问题
    服务端解决浏览器跨域问题
    spring_boot 加入 mybatis
  • 原文地址:https://www.cnblogs.com/sqdmydxf/p/7767823.html
Copyright © 2020-2023  润新知