zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    随笔2
    随笔
    关于updateElement接口
    随笔1
    本地访问正常,服务器访问乱码 记录
    Redis (error) NOAUTH Authentication required.解决方法
    tomcat启动很慢 停留在 At least one JAR was scanned for TLDs yet contained no TLDs.
    微信公众号消息回复
    微信公众号 报token验证失败
    idea中web.xml报错 Servlet should have a mapping
  • 原文地址:https://www.cnblogs.com/x54256/p/8445131.html
Copyright © 2011-2022 走看看