多线程---线程同步

说到线程同步就不得不说电影院卖票的案例:

  • 某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

分析步骤:

  • 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets = 100;

  • 在SellTicket类中重写run()方法实现卖票,代码步骤如下

  • 判断票数大于0,就卖票,并告知是哪个窗口卖的

  • 卖了票之后,总票数要减1

  • 票卖没了,线程停止

  • 定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下

  • 创建SellTicket类的对象

  • 创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称

  • 启动线程

 

代码如下:

 1 public class SellTicket implements Runnable {
 2     private int tickets = 100;
 3     //在SellTicket类中重写run()方法实现卖票,代码步骤如下
 4     @Override
 5     public void run() {
 6         while (true) {
 7             if(ticket <= 0){
 8                     //卖完了
 9                     break;
10                 }else{
11                     try {
12                         Thread.sleep(100);
13                     } catch (InterruptedException e) {
14                         e.printStackTrace();
15                     }
16                     ticket--;
17                     System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
18                 }
19         }
20     }
21 }
22 public class SellTicketDemo {
23     public static void main(String[] args) {
24         //创建SellTicket类的对象
25         SellTicket st = new SellTicket();
26 
27         //创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称
28         Thread t1 = new Thread(st,"窗口1");
29         Thread t2 = new Thread(st,"窗口2");
30         Thread t3 = new Thread(st,"窗口3");
31 
32         //启动线程
33         t1.start();
34         t2.start();
35         t3.start();
36     }
37 }

 

卖票案例的问题:

- 相同的票出现了多次

- 出现了负数的票

原因:线程执行的随机性导致的,可能在卖票过程中丢失cpu的执行权,导致出现问题

同步代码块:

synchronized(任意对象) {
多条语句操作共享数据的代码
}

 

同步代码块的好处与坏处:

  • 好处:解决了多线程的数据安全问题
  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

代码演示:

 1 public class SellTicket implements Runnable {
 2     private int tickets = 100;
 3     private Object obj = new Object();
 4 
 5     @Override
 6     public void run() {
 7         while (true) {
 8             synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
 9                 //t1进来后,就会把这段代码给锁起来
10                 if (tickets > 0) {
11                     try {
12                         Thread.sleep(100);
13                         //t1休息100毫秒
14                     } catch (InterruptedException e) {
15                         e.printStackTrace();
16                     }
17                     //窗口1正在出售第100张票
18                     System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
19                     tickets--; //tickets = 99;
20                 }
21             }
22             //t1出来了,这段代码的锁就被释放了
23         }
24     }
25 }
26 
27 public class SellTicketDemo {
28     public static void main(String[] args) {
29         SellTicket st = new SellTicket();
30 
31         Thread t1 = new Thread(st, "窗口1");
32         Thread t2 = new Thread(st, "窗口2");
33         Thread t3 = new Thread(st, "窗口3");
34 
35         t1.start();
36         t2.start();
37         t3.start();
38     }
39 }

 

 

同步方法:

同步方法:就是把synchronized关键字加到方法上:

修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}

 

同步方法的锁对象是this

同步静态方法:

同步静态方法:就是把synchronized关键字加到静态方法上

修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}

同步静态方法的锁对象是:类名.class

代码演示:

 1 public class MyRunnable implements Runnable {
 2     private static int ticketCount = 100;
 3 
 4     @Override
 5     public void run() {
 6         while(true){
 7             if("窗口一".equals(Thread.currentThread().getName())){
 8                 //同步方法
 9                 boolean result = synchronizedMthod();
10                 if(result){
11                     break;
12                 }
13             }
14 
15             if("窗口二".equals(Thread.currentThread().getName())){
16                 //同步代码块
17                 synchronized (MyRunnable.class){
18                     if(ticketCount == 0){
19                        break;
20                     }else{
21                         try {
22                             Thread.sleep(10);
23                         } catch (InterruptedException e) {
24                             e.printStackTrace();
25                         }
26                         ticketCount--;
27                         System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
28                     }
29                 }
30             }
31 
32         }
33     }
34 
35     private static synchronized boolean synchronizedMthod() {
36         if(ticketCount == 0){
37             return true;
38         }else{
39             try {
40                 Thread.sleep(10);
41             } catch (InterruptedException e) {
42                 e.printStackTrace();
43             }
44             ticketCount--;
45             System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
46             return false;
47         }
48     }
49 }

 

 

Lock锁:


虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

 

 

 

- ReentrantLock构造方法

| 方法名 | 说明 |
| --------------- | -------------------- |
| ReentrantLock() | 创建一个ReentrantLock的实例 |

- 加锁解锁方法

| 方法名 | 说明 |
| ------------- | ---- |
| void lock() | 获得锁 |
| void unlock() | 释放锁 |

 

代码如下:

 1  public class Ticket implements Runnable {
 2       //票的数量
 3       private int ticket = 100;
 4       private Object obj = new Object();
 5       private ReentrantLock lock = new ReentrantLock();
 6 
 7       @Override
 8       public void run() {
 9           while (true) {
10               //synchronized (obj){//多个线程必须使用同一把锁.
11               try {
12                   lock.lock();
13                   if (ticket <= 0) {
14                       //卖完了
15                       break;
16                   } else {
17                       Thread.sleep(100);
18                       ticket--;
19                       System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
20                   }
21               } catch (InterruptedException e) {
22                   e.printStackTrace();
23               } finally {
24                   lock.unlock();
25               }
26               // }
27           }
28       }
29   }
30 
31   public class Demo {
32       public static void main(String[] args) {
33           Ticket ticket = new Ticket();
34 
35           Thread t1 = new Thread(ticket);
36           Thread t2 = new Thread(ticket);
37           Thread t3 = new Thread(ticket);
38 
39           t1.setName("窗口一");
40           t2.setName("窗口二");
41           t3.setName("窗口三");
42 
43           t1.start();
44           t2.start();
45           t3.start();
46       }
47   }

 

 

死锁

概念:

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行

产生死锁的情况:

  1. 资源有限

  2. 同步嵌套

代码如下:

 1 public class Demo {
 2     public static void main(String[] args) {
 3         Object objA = new Object();
 4         Object objB = new Object();
 5 
 6         new Thread(()->{
 7             while(true){
 8                 synchronized (objA){
 9                     //线程一
10                     synchronized (objB){
11                         System.out.println("小康同学正在走路");
12                     }
13                 }
14             }
15         }).start();
16 
17         new Thread(()->{
18             while(true){
19                 synchronized (objB){
20                     //线程二
21                     synchronized (objA){
22                         System.out.println("小薇同学正在走路");
23                     }
24                 }
25             }
26         }).start();
27     }
28 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2020-08-16 19:14  Joker-0927  阅读(143)  评论(0编辑  收藏  举报
// 侧边栏目录