• 线程安全之卖票案例


    线程安全:
    如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的,反之则是线程不安全的。
    卖票案例:
    public class Demo08 {
        public static void main(String[] args) {
            //创建线程任务对象
            Ticket ticket = new Ticket();
            //创建三个窗口对象
            Thread t1 = new Thread(ticket, "窗口1");
            Thread t2 = new Thread(ticket, "窗口2");
            Thread t3 = new Thread(ticket, "窗口3");
            //卖票
            t1.start();
            t2.start();
            t3.start();
        }
        static class Ticket implements Runnable {
            //Object lock = new Object();
            ReentrantLock lock = new ReentrantLock();
            private int ticket = 10;
            public void run() {
                String name = Thread.currentThread().getName();
                while (true) {
                    sell(name);
                    if (ticket <= 0) {
                        break;
                    }
                }
            }
            private void sell(String name) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            if (ticket > 0) { System.out.println(name + "卖票:" + ticket); ticket--; } } } }

    运行之后,结果为:出现了卖重复的票

    分析:创建一个实现类对象,传到三个线程里面,能保证三个线程卖100张票,访问的是共享资源,此时只有一个对象,即单例对象。如果创建三个实现类对象,那么,票就300张了,自己玩自己的,就没有关系了。

    由于这个对象是单例的对象,这种情况下,对象只被创建了一次,从而类中的成员只会初始化一次,这时候你要操作类成员的话,类成员在方法中能够进行改变的时候,单例对象有线程安全问题

    多例对象没有线程安全问题,但是多例对象由于对象被创建了多次,执行效率没有单例对象高单例对象虽然有线程问题,但是之前讲过的代码并没有在方法中修改类成员的值,在service和dao中是没有的,所有单例对象如果没有可以改变的类成员,则是没有线程问题的。

    多个线程访问或多个对象访问且对象的实例只有一个时,类成员会由于第一个对象的修改,后面看到的都是改过的数据.出现了线程安全问题.所以尽量不要定义类成员,而是定义到方法中成为局部变量,一旦定义到方法中去之后,这个问题就随着消失了,即单例对象类中没有可以改变的类成员,则没有线程安全问题.

    单例对象:从始至终只有一个对象实例

    线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写 操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行操作,一般都需要考虑线程同步, 否则的话就可能影响线程安全。

    该案例中,ticket为全局变量,开启了三个线程对全局变量进行写操作,故产生了安全问题。
     如果要解决线程安全问题,做同步进行了,可以使用Lock锁。
    public class Demo08 {
        public static void main(String[] args) {
            //创建线程任务对象
            Ticket ticket = new Ticket();
            //创建三个窗口对象
            Thread t1 = new Thread(ticket, "窗口1");
            Thread t2 = new Thread(ticket, "窗口2");
            Thread t3 = new Thread(ticket, "窗口3");
            //卖票
            t1.start();
            t2.start();
            t3.start();
        }
        static class Ticket implements Runnable {
            //Object lock = new Object();
            ReentrantLock lock = new ReentrantLock();
            private int ticket = 10;
            public void run() {
                String name = Thread.currentThread().getName();
                while (true) {
                    lock.lock();
              try{
                sell(name);
              }
    finally{
                lock.unlock();
              }
    if (ticket <= 0) { break; } } } private void sell(String name) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
    if (ticket > 0) { System.out.println(name + "卖票:" + ticket);
              ticket--; } } } }

    结果如下:

     也可以使用同步方法:

    public class Demo08 {
        public static void main(String[] args) {
            //创建线程任务对象
    
            Ticket ticket = new Ticket();
            //创建三个窗口对象
            Thread t1 = new Thread(ticket, "窗口1");
            Thread t2 = new Thread(ticket, "窗口2");
            Thread t3 = new Thread(ticket, "窗口3");
            //卖票
            t1.start();
            t2.start();
            t3.start();
        }
        static class Ticket implements Runnable {
            //Object lock = new Object();
            ReentrantLock lock = new ReentrantLock();
            private int ticket = 10;
            public void run() {
                String name = Thread.currentThread().getName();
                while (true) {
                    sell(name);
                    if (ticket <= 0) {
                        break;
                    }
                }
            }
            private synchronized void sell(String name) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }if (ticket > 0) {
                    System.out.println(name + "卖票:" + ticket);
                    ticket--;
                }
            }
        }
    }
     
  • 相关阅读:
    数组操作
    HTML CSS 笔记
    jacascript 滚动scroll
    SEO优化技巧
    STP选举规则和例题
    3.1GSM-R的网络组成
    光缆的型号
    光缆的种类
    fdisk命令分区过程
    文件系统管理--挂载光盘与U盘
  • 原文地址:https://www.cnblogs.com/zwh0910/p/14441839.html
Copyright © 2020-2023  润新知