线程安全之卖票案例

线程安全:
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的,反之则是线程不安全的。
卖票案例:
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--;
            }
        }
    }
}
 
原文地址:https://www.cnblogs.com/zwh0910/p/14441839.html