# 多线程安全问题

出现的原因是有多个线程去操作共享资源时,就会出现线程安全问题。

# 火车票售卖

假设有个火车站在售卖火车 100 票

在单个售卖窗口不会出现问题,多个窗口售卖不同的票也不会出现问题,但当多个窗口一起售卖这 100 张票时,就会出现卖出同样编号的票。

出售票
package Base.thread_test;
public class ThreadProblem implements Runnable{
    // 售卖 100 张票
    int ticket = 100;
    @Override
    public void run() {
        while(true){
            try{
                // 此线程休眠,让其他线程有机会运行
                Thread.sleep(2);
            }catch(InterruptedException  e){
                e.printStackTrace();
            }
            if(ticket > 0) {
                // 输出对应的票
                System.out.println(Thread.currentThread().getName() +":"+ ticket--);
            }
        }
    }
    public static void main(String[] args) {
        ThreadProblem t1 = new ThreadProblem();
        Thread thread1 = new Thread(t1,"one");
        Thread thread2 = new Thread(t1,"two");
        Thread thread3 = new Thread(t1,"three");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

在控制台就可以看到

出现这种情况就是

原因是:

  1. 在 CPU 线程的调度分类中,Java 使用的是抢占式调度。
  2. 开启了 3 个线程,3 个线程一起在抢夺 CPU 的执行权,谁能抢到谁就可以被执行。
  3. 看第一个红框,线程 one 抢到了 CPU 的控制权限后,线程 three 也还能输出 5 ,是因为线程 one 在拿到后,也进入了 if 判断里面,此时线程 one ticket=5,但是并没有机会继续将将下一条代码运行完,就被线程 three 就抢夺到了 CPU 的控制权,也进行了自己的运行,此时因为线程 one ticket-- 还没有被执行,所以线程 three ticket 也是 5,因为这样所以才出现了这类情况

# 线程安全的方法

保证线程安全是以是否需要同步手段来分类

# Java 实现线程安全的方法

要实现线程安全,需要保证数据操作的两个特性:

原子性:对数据的操作不会受其他线程打断,意味着一个线程操作数据过程中不会插入其他线程对数据的操作
可见性:当线程修改了数据的状态时,能够立即被其他线程知晓,即数据修改后会立即写入主内存,后续其他线程读取时就能得知数据的变化