00102_线程安全和线程同步

1、线程安全

  (1)如果有多个线程在同时运行,而这些线程可能会同时运行这段代码;

  (2)程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的;

  (3)代码演示:

    ①模拟票

 1 public class Ticket implements Runnable {
 2     // 共100票
 3     int ticket = 100;
 4 
 5     @Override
 6     public void run() {
 7         // 模拟卖票
 8         while (true) {
 9             if (ticket > 0) {
10                 // 模拟选坐的操作
11                 try {
12                     Thread.sleep(1);
13                 } catch (InterruptedException e) {
14                     e.printStackTrace();
15                 }
16                 System.out.println(Thread.currentThread().getName() + "正在卖票:"
17                         + ticket--);
18             }
19         }
20     }
21 }

    ②测试类

 1 public class ThreadDemo {
 2     public static void main(String[] args) {
 3         // 创建票对象
 4         Ticket ticket = new Ticket();
 5 
 6         // 创建3个窗口
 7         Thread t1 = new Thread(ticket, "窗口1");
 8         Thread t2 = new Thread(ticket, "窗口2");
 9         Thread t3 = new Thread(ticket, "窗口3");
10 
11         t1.start();
12         t2.start();
13         t3.start();
14     }
15 }

    ③运行结果:

  

  (4)上面程序出现了问题,错误的票0、-1;

  (5)其实,线程安全问题都是由全局变量及静态变量引起的;

  (6)若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;

  (7)若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

2、线程同步(线程安全处理Synchronized)

  (1)线程同步的方式有两种:

    ①同步代码块;

    ②同步方法

  (2)同步代码块

    ①同步代码块: 在代码块声明上 加上synchronized

synchronized (锁对象) {
    可能会产生线程安全问题的代码
}

    ②同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

    ③使用同步代码块,对卖票案例中Ticket类进行如下代码修改,当使用了同步代码块后,上述的线程的安全问题,解决了。

 1 public class Ticket implements Runnable {
 2     // 共100票
 3     int ticket = 100;
 4     // 定义锁对象
 5     Object lock = new Object();
 6 
 7     @Override
 8     public void run() {
 9         // 模拟卖票
10         while (true) {
11             // 同步代码块
12             synchronized (lock) {
13                 if (ticket > 0) {
14                     // 模拟电影选坐的操作
15                     try {
16                         Thread.sleep(10);
17                     } catch (InterruptedException e) {
18                         e.printStackTrace();
19                     }
20                     System.out.println(Thread.currentThread().getName()
21                             + "正在卖票:" + ticket--);
22                 }
23             }
24         }
25     }
26 }

  (3)同步方法

    ①同步方法:在方法声明上加上synchronized;

1 public synchronized void method(){
2        可能会产生线程安全问题的代码
3 }

    ②同步方法中的锁对象是 this。

    ③使用同步方法,对电影院卖票案例中Ticket类进行如下代码修改,

 1 public class Ticket implements Runnable {
 2     //共100票
 3     int ticket = 100;
 4     //定义锁对象
 5     Object lock = new Object();
 6     @Override
 7     public void run() {
 8         //模拟卖票
 9         while(true){
10             //同步方法
11             method();
12         }
13     }
14 
15 //同步方法,锁对象this
16     public synchronized void method(){
17         if (ticket > 0) {
18             //模拟选坐的操作
19             try {
20                 Thread.sleep(10);
21             } catch (InterruptedException e) {
22                 e.printStackTrace();
23             }
24             System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
25         }
26     }
27 }

  (4)静态同步方法

    ①静态同步方法: 在方法声明上加上static synchronized;

    ②静态同步方法中的锁对象是 类名.class。

public static synchronized void method(){
可能会产生线程安全问题的代码
}

其实,线程安全问题都是由全局变量及静态变量引起的

原文地址:https://www.cnblogs.com/gzdlh/p/8099387.html