zoukankan      html  css  js  c++  java
  • 线程安全问题

    1.线程安全产生的原因

    如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。

    程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    以电影院卖票为例演示线程安全问题

    由三个不同的渠道同时卖100张票

    线程任务:

    public class MyRunnable implements Runnable {
    
        // 卖电影票(共享数据)
        private int ticket = 100;
        public void run() {
            while (true) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
                }
            }
    
        }
    }
    public static void main(String[] args) {
            //创建线程任务
            MyRunnable mr = new MyRunnable();
            //创建线程
            Thread t0 = new Thread(mr);
            Thread t1 = new Thread(mr);
            Thread t2 = new Thread(mr);
            //执行线程
            t0.start();
            t1.start();
            t2.start();
            
        }

    此时执行代码会发现会出现卖第0张和-1张票或者重复卖同一张票的情况,

    原因是某个时间多个线程都执行了同一段if语句中的代码,使得全局变量ticket不准确(操作的票可能被另一个线程卖掉了)

    所以解决这个问题需要在一个线程在执行线程任务时,其他线程不能执行;这也就变得和单线程一样了。

    解决方法:

    (1)同步代码块

    public class MyRunnable implements Runnable {
    
        // 卖电影票(共享数据)
        private int ticket = 100;
        private Object obj = new Object();// 保证锁对象的唯一性
    
        public void run() {
    
            while (true) {
                synchronized (obj) {
                    if (ticket > 0) {
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
                    }
                }
    
            }
    
        }
    
    }

    注意:用synchronized(锁对象){}时, 锁对象要设为全局变量保证其唯一性;

    (2)同步方法

    public class MyRunnable2 implements Runnable {
    
        // 卖电影票(共享数据)
        private int ticket = 100;
        public void run() {
    
            while (true) {
                sale();
    
            }
    
        }
        public synchronized void sale() {
            // 隐含的锁 this
            if (ticket > 0) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
            }
    
        }
    
    }

    注意:将同步代码块封装成一个方法并用synchronized 修饰,这里不需要指定锁对象,这里的锁对象默认为本类对象this

             若果是静态同步方法: 在方法声明上加上static synchronized

       public static synchronized void method(){

      可能会产生线程安全问题的代码

      }

      静态同步方法中的锁对象是 类名.class

    (3)使用lock接口

    public class MyRunnable2 implements Runnable {
    
        // 卖电影票(共享数据)
        private int ticket = 100;
        private Lock lock = new ReentrantLock();//创建锁对象
    
        public void run() {
    
            while (true) {
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");
                }
    
                lock.unlock();
            }
        }
    }

    其实和同步代码块差不多 ,在创建所对象时用Lock接口的子类创建,用lock()和unlock()方法将可能出现安全的代码包起来

  • 相关阅读:
    06 is和==的区别 encode()编码 decode()解码
    05 dic的增删改查 字典的嵌套 考试题dic.get()的相关使用
    03 编码 int ,bool,str的常用操作 主要讲str
    01 基本数据类型 变量 if语句
    04 列表的增删改查 常用方法 元祖 range
    02 while循环 格式化输出 运算符
    多校2 Harmonious Army hdu6598 网络流
    P3159 [CQOI2012]交换棋子 网络流
    P2172 [国家集训队]部落战争 最大流
    P2402 奶牛隐藏 网络流
  • 原文地址:https://www.cnblogs.com/lxzwhite/p/10559327.html
Copyright © 2011-2022 走看看