问题描述

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样
的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
我们通过一个案例,演示线程的安全问题:
电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “葫芦娃大战奥特曼”,本次电影的座位共100个
(本场电影只能卖100张票)。
我们来模拟电影院的售票窗口,实现多个窗口同时卖 “葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票)

  1. 相同的票数,比如5这张票被卖了多回。
  2. 不存在的票,比如0票与-1票,是不存在的。
    这种问题,几个窗口(线程)票数不同步了,这种问题称为线程不安全。
    线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
    解决方案:
    1 . 线程同步代码块
public class Ticket implements Runnable{
    private int ticket = 100;

    Object lock = new Object();
    /*
     * 执行卖票操作
     */
    @Override
    public void run() {
        //每个窗口卖票的操作
        //窗口 永远开启
        while(true){
            synchronized (lock) {
                if(ticket>0){//有票 可以卖
                    //出票操作
                    //使用sleep模拟一下出票时间
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto‐generated catch block
                        e.printStackTrace();
                    }
                    //获取当前线程对象的名字
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"正在卖:"+ticket‐‐);
                }
            }
  1. 同步方法 `
public class Tickets implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        while (true) {
            getTicket();
        }
    }
    public synchronized void getTicket(){//,窗口3(排队)
        if(tickets > 0){//窗口2(排队)
            System.out.println(Thread.currentThread().getName() + " 来取走一张票:" + tickets);
            tickets--;
        }else{
            System.out.println("没票了,都回去吧!");
            System.exit(0);
        }
    }
    //窗口1(释放锁)
}
public class Demo {
    public static void main(String[] args) {
        Tickets ts = new Tickets();

        Thread t1 = new Thread(ts);
        Thread t2 = new Thread(ts);
        Thread t3 = new Thread(ts);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }
}
  1. lock锁
public class Tickets implements Runnable {
    private int tickets = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();//加锁//窗口2(排队),窗口3(排队)
            try {
                if (tickets > 0) {//窗口1(立即锁门)
                    System.out.println(Thread.currentThread().getName() +
								" 来取走一张票:" + tickets);
                    tickets--;
                } else {
                    System.out.println("没票了,都回去吧!");
                    break;
                }
            }finally {
                lock.unlock();//解锁//窗口1(释放锁)
            }

        }
    }
}
public class Demo {
    public static void main(String[] args) {
        Tickets ts = new Tickets();

        Thread t1 = new Thread(ts);
        Thread t2 = new Thread(ts);
        Thread t3 = new Thread(ts);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }
}


10-03 16:04