synchronized原理

synchronized原理

大白话

synchronized锁住一个对象,谁能拿到这个对象谁就有访问权,可以说是得倚天屠龙者得天下,谁拿到谁就说了算。

例子

许多大佬都喜欢拿房子举例子,那么我也来拿这个举例子

  1. 现在有一套房子,你和你的哥们都想进去睡觉,你手快抢到了钥匙(获得锁),于是你就进去,锁上门,开始睡觉了。
  2. 你哥们这时候也想进来啊,于是他去开门,发现门锁了,打不开,他只能在门口等着你(阻塞)。
  3. 你饿了,饿的撑不住了,想出去吃饭(CPU时间片切换)
  4. 但是你怕出去房子就被你哥们占了,所以你爬窗户出去的,门依旧没开锁(CPU时间片切换不会释放锁)
  5. 最终你在房子里吃饱喝足了,你就开门出去了(释放锁)
  6. 出门的时候你把锁给了你哥们(唤醒),你哥们就可以进去进行你刚才的操作。

局部变量

常说局部变量在线程内部,就是线程安全的,真的是这样吗?

先举一个不安全的例子

大家可以试试这段代码,会有各种奇奇怪怪的结果。原因呢,就是因为可能某一次加操作还没完成,就执行了减操作,具体的其实就牵扯到了JAVA代码在JVM中是如何执行的问题。

大概就是 i++在JVM中分成了【获取常数,准备常数,计算常数,保存常数】这四步,CPU时间片会不定时切换,无法保证这四步一口气执行完,所以就会有了不安全的存在

public class Test {

    private static int num = 0 ;
    
    public static void main(String[] args) throws InterruptedException {

        new Thread(()->{
            for (int i = 0; i < 30000 ; i++){
                num ++;
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 30000 ; i++){
                num --;
            }
        }).start();

        System.out.println(num);
    }
}

再举一个线程内局部变量OK的例子,无论如何执行,结果都是 30000 与 -30000,说明是安全的

public class Test {
    private int num = 0;

    public static void main(String[] args) throws InterruptedException {
            Test test1 = new Test();
            Test test2 = new Test();
            new Thread(test1::test1).start();
            new Thread(test2::test2).start();

    }

    private void test1(){
        for (int i = 0; i < 30000 ; i++){
            num ++;
        }

        System.out.println("num:" + num + ",线程:" +Thread.currentThread().getName());
    }

    private void test2(){
        for (int i = 0; i < 30000 ; i++){
            num --;
        }
        System.out.println("num:" + num + ",线程:" +Thread.currentThread().getName());
    }

}

为什么这样就OK呢?实际就是各个线程带着各自的变量各玩各的,互不影响

请添加图片描述
但是我既然记录这个事情,就说明局部变量也有不行的,那就是参数为对象的时候,是有异常的,先上代码!

public class Test {
    List<Integer> list = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        Test test1 = new Test();
        for (int i = 0 ;i<10000;i++){
            new Thread(()->{
                test1.test1();
                test1.test2();
            }).start();
        }
    }

    private void test1(){
        list.add(1);
    }

    private void test2(){
        list.remove(0);
    }

}

大家执行一下会发现,果然是有异常的

Exception in thread "Thread-5252" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:659)
	at java.util.ArrayList.remove(ArrayList.java:498)
	at com.test.main.Test.test2(Test.java:24)
	at com.test.main.Test.lambda$main$0(Test.java:14)
	at java.lang.Thread.run(Thread.java:748)

问题出在哪呢?本质原因就是JVM中存储的位置。八大基础类型是存在于栈的,可以跟着线程玩(这么说也不是很严谨)。但是对象不行,他是存在堆中的,栈帧中只是存了一个指向

在这里插入图片描述
说到这大家看着眼熟吗?相信如果你有开发经验,一定遇到过map或者list循环赋值计算,最后发现都是拿最后的值去处理的这样的bug,本质就是这样的问题,这里不展开这块,后面一起。

浅尝辄止~慢慢来才比较快,共勉!如果哪里不对请及时指出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值