多线程的安全问题

导致线程安全问题的原因:

1、多个线程访问出现延迟

2、线程随机性

注:线程的安全问题在理想状态下,不容易出现,但是一旦出现了对应用系统的影响是非常大的。

 

二、下面我们可以通过Thread.sleep(long time)方法来简单的模拟一下延迟导致的线程安全的问题

如下是一个卖票的多线程代码:

package com.cn.xianChen;

public class SellTicketDemo implements Runnable{
    private int num=50;  //定义票的数量
    @Override
    public void run() {
        for(int i=0;i<100;i++){
             if(num>0){

                 System.out.println(Thread.currentThread().getName()+"卖出第"+num--+"张票了");
            }
        }
    }
}

测试:

package com.cn.xianChen;

public class TestSellTicket {
    public static void main(String[] args) {
        SellTicketDemo st=new SellTicketDemo();
        new Thread(st,"A").start();
        new Thread(st,"B").start();
        new Thread(st,"C").start();
    }
}

结果:

此时一切都是正常的售票。如果因为网络等其它原因导致延迟,下面看看会出现什么问题?

在售票前加入Thread.sleep(1000)每个线程延时1秒,修改后代码如下:

package com.cn.xianChen;

public class SellTicketDemo implements Runnable{
    private int num=25;  //定义票的数量
    @Override
    public void run() {
        for(int i=0;i<100;i++){
             if(num>0){
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 System.out.println(Thread.currentThread().getName()+"卖出第"+num--+"张票了");
            }
        }
    }
}

执行结果如下:

结论:出现卖了0张票,而且还会出现B和C同时卖第22张票的情况,即2个线程抢夺同一资源的问题。即当线程出现延迟时,会导致线程安全问题。

三、如何解决线程的安全问题?

有如下3种方法

方法1、同步代码块

格式如下:

  synchronized (锁对象){
            需要被同步的代码
        }

注:1、锁对象可以是任意的一个对象;2、如果不存在线程安全的问题,千万不要使用同步代码块,因为会降低效率;3、锁对象必须是多线程共享的一个资源,否则锁不住;4、一个线程在同步代码块中sleep了,并不会释放锁对象。

上述代码使用同步代码块解决如下:

public void run() {
        for(int i=0;i<100;i++){
            synchronized (this){
                if (num > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "卖出第" + num-- + "张票了");
                }
            }
        }
    }

方法2、同步方法

同步方法就是用Synchronized修饰一个方法

格式如下:

Synchronized 返回值类型 方法名称(参数列表){
    ....
}

同步方法的同步监听器其实就是this

package com.cn.xianChen;

public class SellTicketDemo implements Runnable{
    private int num=50;  //定义票的数量
    @Override
    public void run() {
        //调用同步方法
        sellTicket();
    }
    /**
     * 同步方法
     * */
    public synchronized void sellTicket(){
        for(int i=0;i<100;i++){
            if (num > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖出第" + num-- + "张票了");
            }
        }
    }
}

注意事项:1、同步方法的锁对象是固定的,不能由你来指定;

                   2、如果是一个非静态的同步函数的锁,对象是this对象

                   3、如果是静态的同步函数的锁,对象是当前函数所属类的字节码文件(即class对象)

方法3、静态方法的同步

 

注:推荐使用同步代码块.原因如下:

1、同步代码块的锁对象可以由我们随意指定,方便控制。而同步方法的锁对象不是固定的,不能由我们控制

2、同步代码块可以很方便的控制需要被同步代码的范围,同步方法必须是整个方法里的所有代码都被同步了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值