00103_死锁、Lock接口、等待唤醒机制

1、死锁

  (1)同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉;

synchronzied(A锁){
    synchronized(B锁){
         
}
}

  (2)代码演示

    ①定义锁对象

1 public class MyLock {
2     public static final Object lockA = new Object();
3     public static final Object lockB = new Object();
4 }

    ②线程任务类

 1 import java.util.Random;
 2 
 3 public class ThreadTask implements Runnable {
 4     int x = new Random().nextInt(1);// 0,1
 5 
 6     // 指定线程要执行的任务代码
 7     @Override
 8     public void run() {
 9         while (true) {
10             if (x % 2 == 0) {
11                 // 情况一
12                 synchronized (MyLock.lockA) {
13                     System.out.println("if-LockA");
14                     synchronized (MyLock.lockB) {
15                         System.out.println("if-LockB");
16                         System.out.println("if大口吃肉");
17                     }
18                 }
19             } else {
20                 // 情况二
21                 synchronized (MyLock.lockB) {
22                     System.out.println("else-LockB");
23                     synchronized (MyLock.lockA) {
24                         System.out.println("else-LockA");
25                         System.out.println("else大口吃肉");
26                     }
27                 }
28             }
29             x++;
30         }
31     }
32 }

    ③测试类

 1 public class ThreadDemo {
 2     public static void main(String[] args) {
 3         // 创建线程任务类对象
 4         ThreadTask task = new ThreadTask();
 5         // 创建两个线程
 6         Thread t1 = new Thread(task);
 7         Thread t2 = new Thread(task);
 8         // 启动线程
 9         t1.start();
10         t2.start();
11     }
12 }

2、Lock接口

  (1)Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作;

  (2)Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能;

  (3)我们使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对卖票案例中Ticket类进行如下代码修改:

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

3、等待唤醒机制

  (1)线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同;

  (2)通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制;

  (3)等待唤醒机制所涉及到的方法:

    ①wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中;

    ②notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的;

    ③notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。

  (4)其实,所谓唤醒的意思就是让线程池中的线程具备执行资格;

  (5)这些方法都是在同步中才有效,同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程;

  (6)仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?

  因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中;

  (7)模拟等待唤醒机制的实现,代码案例:

    ①模拟资源类

 1 public class Resource {
 2     private String name;
 3     private String sex;
 4     private boolean flag = false;
 5 
 6     public synchronized void set(String name, String sex) {
 7         if (flag)
 8             try {
 9                 wait();
10             } catch (InterruptedException e) {
11                 e.printStackTrace();
12             }
13         // 设置成员变量
14         this.name = name;
15         this.sex = sex;
16         // 设置之后,Resource中有值,将标记该为 true ,
17         flag = true;
18         // 唤醒output
19         this.notify();
20     }
21 
22     public synchronized void out() {
23         if (!flag)
24             try {
25                 wait();
26             } catch (InterruptedException e) {
27                 e.printStackTrace();
28             }
29         // 输出线程将数据输出
30         System.out.println("姓名: " + name + ",性别: " + sex);
31         // 改变标记,以便输入线程输入数据
32         flag = false;
33         // 唤醒input,进行数据输入
34         this.notify();
35     }
36 }

    ②输入线程人任务类

 1 public class Input implements Runnable {
 2     private Resource r;
 3 
 4     public Input(Resource r) {
 5         this.r = r;
 6     }
 7 
 8     @Override
 9     public void run() {
10         int count = 0;
11         while (true) {
12             if (count == 0) {
13                 r.set("小明", "男生");
14             } else {
15                 r.set("小花", "女生");
16             }
17             // 在两个数据之间进行切换
18             count = (count + 1) % 2;
19         }
20     }
21 }

    ③输出线程任务类

 1 public class Output implements Runnable {
 2     private Resource r;
 3 
 4     public Output(Resource r) {
 5         this.r = r;
 6     }
 7 
 8     @Override
 9     public void run() {
10         while (true) {
11             r.out();
12         }
13     }
14 }

    ④测试类

 1 public class ResourceDemo {
 2     public static void main(String[] args) {
 3         // 资源对象
 4         Resource r = new Resource();
 5         // 任务对象
 6         Input in = new Input(r);
 7         Output out = new Output(r);
 8         // 线程对象
 9         Thread t1 = new Thread(in);
10         Thread t2 = new Thread(out);
11         // 开启线程
12         t1.start();
13         t2.start();
14     }
15 }

 

posted @ 2017-12-24 22:17  gzdlh  阅读(205)  评论(0编辑  收藏  举报