• 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 }
  • 相关阅读:
    toggle()
    !important
    js 实现向下滑动页面时遇顶固定
    layui多文件选择之后自动上传
    position:fixed和z-index:1
    转:jquery 智能浮动定位smartFloat
    转:DATA URL简介及DATA URL的利弊
    年,月 ,季节 下拉框
    左连接与右连接,外连接与内连接
    git 恢复删去的东西, 以及在本地建个仓库,让远程也有这个仓库
  • 原文地址:https://www.cnblogs.com/gzdlh/p/8099463.html
Copyright © 2020-2023  润新知