JAVA初级(二十七)多线程(3)线程安全与线程同步synchronized的基本使用

线程安全就是说当多个线程同时运行同一段代码时,可能会导致运行后的结果和预期的不一致。

线程安全问题

模拟一个线程安全问题的场景。假如有一个货品只有100件库存。客户们通过抢货来购买

public class Goods implements Runnable{
    private int num = 100;//100个货
    @Override
    public void run() {
        while (true){
                if(num>0){
                    num--;
                    System.out.println(Thread.currentThread().getName()+"买到了货,库存:"+num);
                }else {
                    break;
                }
        }
    }
}

 public static void main(String[] args){
 		//3个客户抢货
        Goods goods = new Goods();
        Thread thread1 = new Thread(goods,"客户1");
        Thread thread2 = new Thread(goods,"客户2");
        Thread thread3 = new Thread(goods,"客户3");
        thread1.start();
        thread2.start();
        thread3.start();
    }

执行以上代码结果如下所示:
在这里插入图片描述
在这里插入图片描述
正常情况应该是99,98,97这样。但实际不是这样。甚至还出现了相同的库存输出。这是由于多个线程运行同一段代码出现的问题。

分析:
1,出现这种情况主要的线程之间的CPU资源竞争。客户1买了货成功扣减了库存,此时num是99,但还没来得及输出就被客户2打断。这样客户2
拿到了num就是99,这没有问题。然后又是还没来得及输出被客户3打断.此时就拿到了num是98.然后还是没来得及输出。被客户1抢回CPU资源。此时它拿到的num就是97,然后接着刚才被打断的地方输出。然后就输出了97(原本应该是99)

–>如果在还没有执行num–;这行代码就被打断。等这个线程再次抢到资源的时候很有可能num已经是0的。这时候如果继续执行那就会导致意外结果.

比如看以下代码,修改线程代码如下

public class Goods implements Runnable{
    private int num = 100;//100个货
    @Override
    public void run() {
        while (true){
                if(num>0){
                //添加睡眠,强制让出cpu资源
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num--;
                    System.out.println(Thread.currentThread().getName()+"买到了货,库存:"+num);
                }else {
                    break;
                }
        }
    }
}

然后你在执行一次,就会输出以下错误的结果。
在这里插入图片描述
造成这个结果的原因主要是因为多个线程对同一个全局变量进行了写的操作导致的。

synchronized关键字解决线程安全问题

1,同步代码块处理上述的线程安全问题
synchronized (锁对象) {
可能会出现线程安全问题的代码;
}
锁对象就相当于一把锁。只有拿到锁的人才能执行代码块里面的代码。
过程大概就是客户1拿到这个对象锁进入到代码块。然后执行到一半.客户2进来了发现拿不到锁所以在门外等待客户1执行完。客户1执行完后把锁释放。接着客户2拿到锁进去执行。
也就是拿锁->执行代码块->放锁

synchronized代码块使用如下所示:

public class Goods implements Runnable{
    private int num = 100;//100个货
    Object obj = new Object();//创建一个实例对象
    @Override
    public void run() {
        while (true){
            synchronized (obj){//放一个实例对象锁
                if(num>0){
                    num--;
                    System.out.println(Thread.currentThread().getName()+"买到了货,库存:"+num);
                }else {
                    break;
                }
            }
        }
    }
}

然后再次执行上面的main方法
在这里插入图片描述
在这里插入图片描述
这样就能准确的输出结果,

synchronized同步方法

synchronized也可以用来修饰方法,这个方法就会变成同步方法
使用方法如下

public synchronized void fun(){
   	可能会产生线程安全问题的代码
}

同步方法的锁对象是.class也就是类对象

修改线程代码如下:

public class Goods implements Runnable{
    private int num = 100;//100个货
    Object obj = new Object();//创建一个实例对象
    @Override
    public void run() {
        while (true){
           fun();
           //记得自己添加跳出循环的代码
        }
    }

//同步方法
    public synchronized void  fun(){
        if(num>0){
            num--;
            System.out.println(Thread.currentThread().getName()+"买到了货,库存:"+num);
        }
    }
}

然后执行main方法,结果是一样的。
到这线程安全和synchronized的基本知识就介绍完了

总结:
1,线程安全问题是什么?
多个线程同时执行同一段代码时,可能会出现预期之外的结果。这就是线程安全问题
2,synchronized同步代码块
synchronized(对象锁){
可能会出现线程安全问题的代码
}
3,synchronized同步方法
public synchronized void fun(){
可能会出现线程安全问题的代码
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值