多线程操作共享数据出现的安全问题(卖票案例)

操作共享数据,线程不安全代码:

package com.yonyou.sci.gateway.exec;

public class SellTicket implements Runnable{

    private static int ticket = 1;

    @Override
    public void run () {
        while (true) {
            if (ticket > 0) {
                try{
                    // 模拟在这个位置,线程的执行权限被其他线程抢走了
                    Thread.sleep(10);
                }catch (Exception ex){}

                System.out.println(Thread.currentThread().getName()+" 出售第 " + ticket-- +" 票");
            }
        }
        
    }

}

三个线程同时执行上面的任务:

package com.yonyou.sci.gateway.exec;

public class SellMain {

    public static void main (String[] args) {
        SellTicket st = new SellTicket();

        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        Thread t3 = new Thread(st);

        t1.start();t2.start();t3.start();
    }
}

结果:

Thread-0 出售第 1 票
Thread-2 出售第 0 票
Thread-1 出售第 -1 票

分析:

备注:线程的执行并不是按照顺序执行的,哪个线程抢到CPU执行权限,就执行哪个线程,所以上面的线程名不是按照顺序的。

1.Thread-0 抢到CPU执行权限、开始执行任务,判断剩余票数大于0,sleep后、线程Thread-1 开始执行;

2.Thread-2 抢到CPU执行权限、开始执行任务,Thread-0 还没有运行到 ticket-- 代码,此时 ticket 还是为1,判断剩余票数大于0,sleep后、线程Thread-1 开始执行;

3.Thread-1 抢到CPU执行权限、开始执行任务,Thread-0、Thread-2 都还没有运行到 ticket-- 代码,此时 ticket 还是为1,判断剩余票数大于0,sleep后、线程Thread-0 开始执行 ticket-- 代码,输出:Thread-0 出售第 1 票

4.Thread-2 开始执行 ticket-- 代码,此时 Thread-0 已经运行 ticket-- 代码,ticket变量值为0,所以输出:Thread-2 出售第 0 票

5.Thread-1 开始执行 ticket-- 代码,此时 Thread-0、Thread-2 已经运行 ticket-- 代码,ticket变量值为 -1,所以输出:Thread-1 出售第 -1 票;


解决线程异步操作共享数据安全问题:

方法一:增加 synchronized 代码块;

方法二:在方法上增加 synchronized 关键字;

方法三:使用 lock 锁;

扫描二维码关注公众号,回复: 7547348 查看本文章

方法一实现代码:

package com.yonyou.sci.gateway.exec;

public class SellTicket implements Runnable{

    private static int ticket = 1;

    @Override
    public void run () {
        while (true) {
            synchronized (this){
                if (ticket > 0) {
                    try{
                        // 模拟在这个位置,线程的执行权限被其他线程抢走了
                        Thread.sleep(10);
                    }catch (Exception ex){}

                    System.out.println(Thread.currentThread().getName()+" 出售第 " + ticket-- +" 票");
                }
            }
        }

    }

}

 方法三 lock 锁实现代码:

线程类:

package com.yonyou.sci.gateway.exec.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicketLock implements Runnable{

    private int ticket = 1;

    private Lock lock = new ReentrantLock();

    @Override
    public void run () {

        while (true) {
            lock.lock();
            try{
                if (ticket > 0) {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName()+" 出售第 " + ticket-- +" 票");
                }
            }catch (Exception ex){
                System.out.println(ex.getMessage());
            }finally {
                lock.unlock();
            }


        }
    }

}

执行线程:

package com.yonyou.sci.gateway.exec.lock;

public class SellLockMain {

    public static void main (String[] args) {
        SellTicketLock st = new SellTicketLock();

        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        Thread t3 = new Thread(st);

        t1.start();t2.start();t3.start();
    }
}

猜你喜欢

转载自www.cnblogs.com/tangshengwei/p/11723649.html
今日推荐