java多线程5:线程同步机制(synchronized)

线程同步

在单线程程序中,后面的线程只能到前面的线程执行完毕才能执行,这不会出现线程抢占问题。但是在多任务的操作系统中,不同优先级的线程抢占CPU资源,这会造成线程共享资源出现资源冲突的问题

我们来引入一个场景:秒杀我们都经常见到过。假如有2个人抢一台手机,势必会造成有一个能抢到,有一个抢不到。假如A运气比较好,抢到了,那么手机数量就会-1,进而变成0,这时候B再来秒杀手机时,程序询问手机数量发现为0,进而反馈:运气真不好,手机被抢走了。

但是如果没有线程同步,当A购买了手机,此时应该手机要减一,但是线程A并没有修改。B也打算下单,程序询问手机数量发现手机还是1(按理来说应该是0),也下单成功。此时就会造成,明明只有一台手机,但是有两个人下单成功的尴尬局面。

因此线程同步是个非常重要的问题:场景也有很多(火车票售卖、淘宝库存、排号系统等等)

这种都必须要求,只有一个线程能访问线程共享资源(剩余票数,库存数,资源剩余),等他执行完毕,才允许其他线程访问

我们来通过买票问题来说明问题

一开始没有线程同步时

public class TreadSafe implements Runnable{

    int num=10;
    @Override
    public void run() {
        while (true){
            if(num>0){
                try {
                    Thread.sleep(100);
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("tickets:"+num--);
            } 
        }
    }
    public static void main(String [] args){
        TreadSafe tread =new TreadSafe();
        Thread t1=new Thread(tread);
        Thread t2=new Thread(tread);
        Thread t3=new Thread(tread);
        Thread t4=new Thread(tread);
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

到最后你会发现tickets

在这里插入图片描述

这就是各个线程没有同步的后果,线程1234都有各自的tickets数值,但互相没有同步,会恶意修改num值

那就会出现明明线程1拿走最后一张票了,线程2访问时发现自己的tickets并不是0,继续访问if{}

线程锁

既然问题出现了,那么就有解决问题的方法,那就是给共享资源(票数)上一道锁,当一个线程访问共享资源时,上一道锁,其他线程只能等待前面的线程执行完毕才可以访问。

synchronized关键字可以实现,是基于悲观锁设计的,被synchronized标注的代码块只允许一个线程能访问资源,其他线程等待前面的线程执行完才能进入。

synchronized有两种表示方法

一种是synchronized代码块;一种是synchronized方法

先看第一种

public class TreadSafe implements Runnable{

    int num=10;
    @Override
    public void run() {
        while (true){
            synchronized (""){
                if(num>0){
                    try {
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println("tickets:"+num--);
                }
            }
        }
    }
    public static void main(String [] args){
        TreadSafe tread =new TreadSafe();
        Thread t1=new Thread(tread);
        Thread t2=new Thread(tread);
        Thread t3=new Thread(tread);
        Thread t4=new Thread(tread);
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

我们在之前的例子 中while加入synchronized代码块

synchronized(object){
	//...
}

其实现原理就是,Object为一个任意对象,在每个对象里都有一个标志位(o or 1)。当一个线程访问该代码块时,会先访问对象的标志位,如果标志位为0,说明有其他线程在执行,就自己进入就绪状态直到标志位为1,才能进入该代码块执行,此时标志位又会从1变成0;该代码区又会称为临界区。

这样就能解决问题

在这里插入图片描述

还有一种就是synchronized方法,在前面加上synchronized修饰的方法

synchronized void f(){}

在之前的案例中创建一个类文件,在该类里定义同步方法

public synchronized void doit(){
	if(num>0){
         try {
              Thread.sleep(100);
          }catch (Exception e){
              e.printStackTrace();
          }
          System.out.println("tickets:"+num--);
    }
}

public void run(){
    while(ture){
        doit();
    }
}

这样执行的结果与代码块无异。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你们卷的我睡不着QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值