线程安全---synchronized

直接上代码

  private static int a = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for(int i = 0; i < 50000; i++){
                    a++;
            }
        });

        Thread t2 = new Thread(() -> {
            for(int i = 0; i < 50000; i++){
                    a++;
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("a = "+a);
    }

启动t1,t2线程,期望结果a = 100000,但结果不会陪你演戏


结果不为100000就算了,每次的结果都还不同

下面我们就来分析一下原因:

a++操作,往细分,我们可以分为三小步来看

1.load :加载a的值     ++:在a值基础上再加1     save:保存值

我们知道,cpu中有一个部件名为寄存器,其访问速度更快,容量更小,cpu做运算一般都是根据寄存器上的数据,下面为a++操作的图示

由图可以看出,明明是两个a++操作,实际上却只增加了1,其原因就是因为操作是非原子性所造成的,造成了数据的脏读(读到的另一个线程还没有提交到的数据)

这个时候我们就能理解为什么a最后的值不为100000了

那么a的值一定会大于50000吗?

我们首先来分析出现大于50000的时候是什么情况

就是线程调度中最坏的情况即为,两个线程调度正好出现 自增两次 但实际只自增1,如果每次都是这样,那么a就是50000,如果只是部分情况为这样,那么就为大于

会出现a小于50000的情况吗?

依据以上的分析,我们发现  是可能会的

即为当t1线程自增1下的情况下,t2线程自增两次or三次,这个时候a的值就小于50000


如何解决以上问题呢:

我们知道问题的本质即为操作非原子性,可以拆分 load/add/save,那么我们就将操作变成原子性是否就迎刃而解了   (所谓原子,就是不可再拆分的操作单位)

解决方案:synchronized

什么是synchronized

这是一个关键字,直译为同步/同时化  在这代表加锁

 private static int a = 0;
    public static void main(String[] args) throws InterruptedException {
        Object loker = new Object();
        Thread t1 = new Thread(() -> {
            for(int i = 0; i < 50000; i++) {
                synchronized (loker) {
                    a++;
                }
            }
        });
        
        Thread t2 = new Thread(() -> {
            for(int i = 0; i < 50000; i++){
                synchronized (loker){
                    a++;
                }
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("a = ");
    }

程序竟然正常运行了,那么这个synchronized到底有什么魔力呢

synchronized的使用方法:

1.修饰方法

        1).普通方法

        2).静态方法

2.修饰代码块

进入方法就加锁,离开方法就释放锁,需要注意的是,一定要搞清楚锁对象是谁

对于普通方法来说,锁对象是this,对于静态方法来说,锁对象是类对象(.class),对于代码块来说,锁对象是手动指定的锁对象

所以上面的累加a操作也可以在方法中实现

private static int a = 0;
    synchronized public void add(){
        a++;
    }
    public static void main(String[] args) throws InterruptedException {
        ThreadSecurity th = new ThreadSecurity();
        Thread t1 = new Thread(() -> {
            for(int i = 0; i < 50000; i++){
                th.add();
            }
        });
        Thread t2 = new Thread(() -> {
            for(int i = 0; i < 50000; i++){
                th.add();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(a);
    }

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值