博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java——线程锁,死锁,等待唤醒机制
阅读量:4568 次
发布时间:2019-06-08

本文共 4891 字,大约阅读时间需要 16 分钟。

一、线程锁

线程安全问题

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

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

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

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     @Override12     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

 

转载于:https://www.cnblogs.com/x54256/p/8445131.html

你可能感兴趣的文章
为什么主引导记录的内存地址是0x7C00?
查看>>
第九节:web爬虫之urllib(五)
查看>>
三种循环的介绍
查看>>
浅谈Sql Server中的 隔离层级(Isolation Levels)
查看>>
{{ }} 的替换 delimiters
查看>>
C语言习题三
查看>>
94. Binary Tree Inorder Traversal
查看>>
MongoDB安装及多实例启动
查看>>
[css]我要用css画幅画(三)
查看>>
JSP与Servlet之间传值
查看>>
java第五次上机(异常)
查看>>
eletron打包
查看>>
Sencha touch 开发系列:组件布局
查看>>
MySQL的时间进位问题
查看>>
EasyShortcut Easyshortcut easyShortcut 简介
查看>>
可以直接调用的纯虚函数
查看>>
POJ 2975 Nim 尼姆博弈
查看>>
vue 过渡效果
查看>>
url 地址含参数较多如何拼接
查看>>
UIContainerView纯代码实现及原理介绍
查看>>