zoukankan      html  css  js  c++  java
  • 多线程操作共享数据出现的安全问题(卖票案例)

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

    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 锁;

    方法一实现代码:

    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();
        }
    }
  • 相关阅读:
    数值拓展与函数拓展
    正则小记
    Struts2(三)配置详解
    Struts2(二)工作原理
    Struts2(一)基本配置
    Eclipse新建动态web工程项目出现红叉解决方案
    Spring系列之AOP
    Java动态代理的实现机制
    Spring系列之装配Bean
    Spring系列之基本配置
  • 原文地址:https://www.cnblogs.com/tangshengwei/p/11723649.html
Copyright © 2011-2022 走看看