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()方法将可能出现安全的代码包起来

  • 相关阅读:
    传智博客.NET培训第13季 Ajax教程(共十三季) 学习资源
    一些sql语句的常用总结(重要)
    处理oracle的死锁
    Adroid 总结--android ListView美化,个性化更改的属性
    如何远程备份sql server数据库
    VSS (Visual Source Safe 2005) 用法详解
    php插入代码数据库
    PHP之PHP文件引用详解
    需要引入库:vue-resource
    axios调用详解
  • 原文地址:https://www.cnblogs.com/lxzwhite/p/10559327.html
Copyright © 2011-2022 走看看