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();
        }
    }
  • 相关阅读:
    c# asp.net 鼠标改变控件坐标位置,更改控件坐标,注册表保存读取,打印,查找局域网内打印机等等收集
    jquery,extjs中的extend用法小结
    JavaScript创建Map对象(转)
    ExtJS与jQuery的一点细节上的对比
    jQuery 的原型关系图,整体把握jQuery
    github网站介绍、并使用git命令管理github(详细描述)
    1.移植3.4内核-分析内核启动过程,重新分区,烧写jffs2文件系统
    6.移植uboot-支持yaffs烧写,打补丁
    5.移植uboot-设置默认环境变量,裁剪,并分区
    4.移植uboot-使uboot支持DM9000网卡
  • 原文地址:https://www.cnblogs.com/tangshengwei/p/11723649.html
Copyright © 2011-2022 走看看