乐趣区

java多线程-线程安全问题

当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题

模拟线程安全问题

public class SafeThread implements Runnable {

    private int ticketCount = 50;

    @Override
    public void run() {while (ticketCount > 0) {
            try {Thread.sleep(50);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ", 出售第" + (50 - ticketCount + 1) + "张票");
            ticketCount--;
        }
    }
}
@RequestMapping("test-safe")
    public void testSafe() {SafeThread safeThread = new SafeThread();
        Thread t1 = new Thread(safeThread, "thread-1");
        Thread t2 = new Thread(safeThread, "thread-2");
        t1.start();
        t2.start();}

结果:火车票会重复出售

解决办法

使用多线程之间同步 synchronized 或使用锁(lock)
1. 同步代码块

public class SafeThread implements Runnable {

    private int ticketCount = 50;

    @Override
    public void run() {while (ticketCount > 0) {
            try {Thread.sleep(50);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            synchronized (this) {System.out.println(Thread.currentThread().getName() + ", 出售第" + (50 - ticketCount + 1) + "张票");
                ticketCount--;
            }
        }
    }
}

2. 同步方法

public class SafeThread implements Runnable {

    private int ticketCount = 50;

    @Override
    public void run() {while (ticketCount > 0) {
            try {Thread.sleep(50);
            } catch (InterruptedException e) {e.printStackTrace();
            }
//            synchronized (this) {//                System.out.println(Thread.currentThread().getName() + ", 出售第" + (50 - ticketCount + 1) + "张票");
//                ticketCount--;
//            }
            sale();}
    }

    private synchronized void sale() {System.out.println(Thread.currentThread().getName() + ", 出售第" + (50 - ticketCount + 1) + "张票");
        ticketCount--;
    }
}

注意:同步函数使用 this 锁

3. 静态同步函数
方法上加上 static 关键字,使用 synchronized 关键字修饰或者使用类.class 文件。
静态的同步函数使用的锁是该函数所属字节码文件对象
可以用 getClass 方法获取,也可以用当前类名.class 表示

public class SafeThread implements Runnable {

    private int ticketCount = 50;

    @Override
    public void run() {while (ticketCount > 0) {
            try {Thread.sleep(50);
            } catch (InterruptedException e) {e.printStackTrace();
            }
//            synchronized (this) {//                System.out.println(Thread.currentThread().getName() + ", 出售第" + (50 - ticketCount + 1) + "张票");
//                ticketCount--;
//            }
//            sale();
            sale2();}
    }

    private synchronized void sale() {System.out.println(Thread.currentThread().getName() + ", 出售第" + (50 - ticketCount + 1) + "张票");
        ticketCount--;
    }


    private void sale2() {synchronized (SafeThread.class) {System.out.println(Thread.currentThread().getName() + ", 出售第" + (50 - ticketCount + 1) + "张票");
            ticketCount--;
        }
    }
}
退出移动版