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

一、线程锁

线程安全问题

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

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

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

1、同步代码块

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

公式:

synchronized(任意对象){

  线程要操作的共享数据

}

调用类

1

2

3

4

5

6

7

8

9

10

11

12

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();

    }

}

同步代码块

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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(){

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

}

同步方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

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 }

复制代码

复制代码

 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 }

复制代码

复制代码

 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 }

复制代码

复制代码

1 package cn.x5456.demo;
2 
3 public class Resource {
4     String name;
5     String sex;
6     boolean flag = false;
7 }

复制代码

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/386555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

资深大牛带你了解源码!关于Android程序员最近的状况,大厂内部资料

前言 回顾一下自己这段时间的经历,因公司突然通知裁员,我匆匆忙忙地出去面了几家,但最终都没有拿到offer,我感觉今年的寒冬有点冷。公司开始第二波裁员,我决定主动拿赔偿走人。后续的面试过程我做了一些准备&#xff…

AE 新建项目(一)(持续更新,做到哪算哪)

开发环境 工具:Visual Studio 2012、ArcEngine10.4.1 语言:C# 开发步骤 1、打开Visual Studio 2012,选择新建项目,创建一个.NET Framework4的,Windows窗体应用程序。取名Demo 2、工具箱中,拖一个splitConta…

基于redis分布式锁实现的多线程并发程序

前两个版本的代码 都或多或少存在一定的问题,虽然可能微乎其微,但是程序需要严谨再严谨, 第一个版本问题: 局限于单机版,依赖于 Jvm的锁 第二个版本问题: 极端情况下,解锁逻辑的问题&#xf…

day15 Ui自动化元素的定位

day15 元素的定位Ui自动化元素的定位1、火狐浏览器安装try xpath2、元素定位思路:(1)查看页面元素,确认能够唯一定位到元素的属性,比如id,文案3、学习xpath cssSelector 手写定位方式xpath(xpat…

几张图可以理解GC JVM调优的内容

public class ApiPurchaseOrderServiceApp {public static void main(String[] args) throws Exception {ApiPurchaseOrderServiceApp mnew ApiPurchaseOrderServiceApp();m.compute();//栈System.out.println("ok");//方法出口}public int compute(){int a1;//局部…

灵魂拷问!一起刷完了这份1307页的安卓面试宝典吧,不吃透都对不起自己

前言 每个程序员都有一个梦想,那就是进一线互联网公司深造,不要跟我说你不想进去,如果给你一个这样的平台,不管是薪资待遇还是接触的高度来说,对我们程序员来说都是一个机会,我以前有一个同事,…

ShardingSphere分库分表实战

ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于…

灵魂拷问!细数Android开发者的艰辛历程,成功入职阿里

什么是中年危机 根据权威数据显示,国内IT程序员鼎盛时期是在25-27岁左右,30岁对于程序员而言完全是一个38线,接着就是转业转岗的事情,这一点在业界也算是一个共识了。 大学毕业步入IT行业普遍年龄也是在22岁左右,然而…

JMM模型到并发编程

电脑:内存 L1 L2 L3 缓存 CPU ctrlatldel就可以看到 Java 有线程内存,在执行线程的时候,会从主内存把变量加载到工作内存(缓存),所以,在多线程同时改变一个静态变量时候,实际是分开…

焦虑的移动互联网开发者如何破局?专题解析

尴尬的35岁 不知道是哪个人提出的职场35岁就要面临被淘汰的定律,因为35岁定律本来就是个伪命题,尤其是在IT行业! 现在年八九百万的大学生毕业,他们虽然年轻活力,但是很多企业也将之“拒之门外”。 35岁的不要,二十几…

17.前端路由router-07keep-alive

keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能, 由于是一个抽象组件,所以在v页面渲染完毕后不会被渲染成一个DOM元素 当组件在keep-alive内被切换时组件的activated、deactivated这两个生命 周期钩子函数会…

使用IDEA创建Maven项目和Maven使用入门(配图详解)

本文详解的讲解了使用IDEA创建Maven项目,及Maven的基础入门。 1、打开IDEA,右上角选择File->New->Project 2、如图中所示选择Maven(可按自己所需添加,否则加载时速度很慢) 3、添加项目所需Groupld,ArtifactId,Ve…

jvisualvm安装Visual GC插件

给jdk自带的jvisualvm安装Visual GC插件,遇到Were sorry the java.net site has closed(我们很抱歉java.net网站已经关闭) 1、找到新的更新地址 visualvm新访问地址:https://visualvm.github.io/index.html 进入“Plugins”&…

来自阿里巴巴佛系安卓程序员的指南,专题解析

开头 中国互联网发展的这些年,如今90后程序员是中国程序员的主力军,互联网的热潮也让一批批00后蠢蠢欲动,尝试涌入互联网圈。 当程序员容易,当一个优秀的程序员需要不断学习,从初级程序员到高级程序员,从…

C#在WinForm中打开控制台显示

引用: namespace 测试使用 {public partial class Form1 : Form{[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError true)][return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bo…

Redis+AQS

前言 对于java的单进程应用来说,存在资源竞争的场景可以使用synchronized关键字和Lock来对资源进行加锁,使整个操作具有原子性。但是对于多进程或者分布式的应用来说,上面提到的锁不共享,做不到互相通讯,所以就需要分…

disruptor 介绍

一、背景 1.来源 Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内部的内存队列的延迟问题,而不是分布式队列。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。…

算法题+JVM+自定义View,详细的Android学习指南

前言 想要成为一名优秀的Android开发,你需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~。 学算法真的很痛苦,虽然大数据现在很火,但找到适合自己定位的职业也未尝不是一种合理选择。 投百度的经历非…

用过的前端插件合集

用过的前端插件合集 FontAwesome字体 Font Awesome详细用法参见上述站点的Examples。 SweetAlert系列 SweetAlertSweetAlert2SweetAlert 到 SweetAlert2 升级指南示例: 基本使用: swal("标题","内容","success);使用SweetAlert…

CAS和AQS

CAS 全称(Compare And Swap),比较交换 Unsafe类是CAS的核心类,提供硬件级别的原子操作。 // 对象、对象的地址、预期值、修改值 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);缺点&#xf…