zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    file is universal (3 slices) but does not contain a(n) armv7s slice error for static libraries on iOS
    WebImageButton does not change images after being enabled in Javascript
    ajax OPTION
    编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
    获取海洋天气预报
    C#线程系列教程(1):BeginInvoke和EndInvoke方法
    js控制只能输入数字和小数点
    Response.AddHeader(,)
    ManualResetEvent的理解
    Convert.ToInt32、int.Parse(Int32.Parse)、int.TryParse、(int) 区别
  • 原文地址:https://www.cnblogs.com/gzdlh/p/8099463.html
Copyright © 2011-2022 走看看