• Java——线程锁,死锁,等待唤醒机制


    一、线程锁

    线程安全问题

    其实,线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

    由于线程休眠的特性,从哪休眠就从哪继续执行(一个线程的事情还没干完就被其他线程挤下去了),回来继续干就会导致操作的全局变量或静态变量出现问题。

    为了解决这个问题,我们就需要让线程执行完毕(不能被其他线程挤下去),以下是几种解决办法。

    1、同步代码块

    保证代码块执行完毕,再切换线程。

    公式:

    synchronized(任意对象){

      线程要操作的共享数据

    }

    调用类

    public class ThreadDemo {
        public static void main(String[] args) {
            Ticket tk = new Ticket();
            Thread t01 = new Thread(tk);
            Thread t02 = new Thread(tk);
            Thread t03 = new Thread(tk);
    
            t01.start();
            t02.start();
            t03.start();
        }
    }
    

    同步代码块

    public class Ticket implements Runnable {
        //定义出售的票源
        private int ticket = 100;
    
        public void run() {
            while (true) {
                // 因为里面可以填任意对象,所以可以使用this(表示当前实例化的Ticket对象tk)
                synchronized (this) {
                    //对票数判断,大于0,可以出售,变量--操作
                    if (ticket > 0) {
                        System.out.println(Thread.currentThread().getName() + " 出售第 " + ticket--);
                    }
                }
            }
        }
    }

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

    2、同步方法

    还可以将需要同步的代码块,抽出来一个方法,使用synchronized字段修饰。

    public synchronized void method(){

      可能会产生线程安全问题的代码

    }

    同步方法

    public class Ticket implements Runnable {
        //定义出售的票源
        private int ticket = 100;
    
        public void run() {
            while (true) {
                func();
            }
        }
    
        private synchronized void func() {
            //对票数判断,大于0,可以出售,变量--操作
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 出售第 " + ticket--);
            }
        }
    }

    同步方法中的锁对象是this,如果是静态同步方法的话同步锁是本类类名.class

    3、Lock接口

    public class Ticket implements Runnable{
    
        //定义出售的票源
        private int ticket = 100;
        //在类的成员位置,创建Lock接口的实现类对象
        private Lock lock = new ReentrantLock();
    
        public void run(){
            while(true){
                //调用Lock接口方法lock获取锁
                lock.lock();
                //对票数判断,大于0,可以出售,变量--操作
                if( ticket > 0){
                    try{
                        //执行可能会引发线程安全问题的代码
                        System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);
                    }catch(Exception ex){
    
                    }finally{
                        //释放锁,调用Lock接口方法unlock
                        lock.unlock();
                    }
                }
            }
        }
    }
    

    二、死锁

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

    三、等待唤醒机制

    线程之间的通信:

    多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即——等待唤醒机制。

    等待唤醒机制

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

    其实,所谓唤醒的意思就是让线程池中的线程具备执行资格。必须注意的是,这些方法都是在同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。

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

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

     1 package cn.x5456.demo;
     2 
     3 public class ThreadDemo {
     4     public static void main(String[] args) {
     5         Resource r = new Resource();
     6 
     7         // 共享数据
     8         Input in = new Input(r);
     9         Output out = new Output(r);
    10 
    11         Thread tin = new Thread(in);
    12         Thread tout = new Thread(out);
    13 
    14         tin.start();
    15         tout.start();
    16     }
    17 }
    ThreadDemo
     1 package cn.x5456.demo;
     2 
     3 public class Input implements Runnable{
     4     private Resource r;
     5     int i = 0;
     6 
     7     public Input(Resource r){
     8         this.r=r;
     9     }
    10 
    11 
    12     public void run() {
    13         while (true){
    14             synchronized (r){   //要使用同一个对象来看着Input和Output两个同步方法(否则就各自走各自的了)
    15                 if(r.flag){
    16                     try {
    17                         r.wait();   //使用同一个对象才能等待+启动
    18                     } catch (InterruptedException e) {
    19                         e.printStackTrace();
    20                     }
    21                 }
    22                 if(i%2==0){
    23                     r.name = "张三";
    24                     r.sex = "男";
    25                 }else{
    26                     r.name = "lisi";
    27                     r.sex = "nv";
    28                 }
    29                 i++;
    30                 r.flag = true;
    31                 r.notify();     //唤醒另一边
    32             }
    33         }
    34     }
    35 }
    Input
     1 package cn.x5456.demo;
     2 
     3 public class Output implements Runnable{
     4     private Resource r;
     5 
     6     public Output(Resource r) {
     7         this.r = r;
     8     }
     9 
    10 
    11     @Override
    12     public void run() {
    13         while (true){
    14             synchronized (r){
    15                 if(!r.flag){
    16                     try {
    17                         r.wait();
    18                     } catch (InterruptedException e) {
    19                         e.printStackTrace();
    20                     }
    21                 }
    22                 System.out.println(r.name+".."+r.sex);
    23                 //标记改成false,唤醒对方线程
    24                 r.flag = false;
    25                 r.notify();
    26             }
    27         }
    28     }
    29 }
    Output
    1 package cn.x5456.demo;
    2 
    3 public class Resource {
    4     String name;
    5     String sex;
    6     boolean flag = false;
    7 }
    Resource
  • 相关阅读:
    【WPF】 前言
    【设计模式】 建造者
    拖动调整显示框的显示区域大小
    HTTP权威指南
    input自动获取焦点
    元素JS拖动的实现
    手机端html5触屏事件(touch事件)
    使用".."指定git提交范围与"..."指定git提交范围的区别
    Long-term stable release maintenance
    MEMS--微机电系统
  • 原文地址:https://www.cnblogs.com/x54256/p/8445131.html
Copyright © 2020-2023  润新知