Java 多线程

synchronized关键字用于使类或方法线程安全,这意味着只有一个线程可以锁定同步方法并使用它,其他线程必须等到锁定释放并且其中任何一个获得该锁定。

如果我们的程序在多线程环境中运行,并且两个或多个线程同时执行,则使用它非常重要。但有时它也会导致一个叫做死锁的问题。

Java中死锁的介绍

操作系统中的进程使用不同的资源并以下列方式使用资源。

1)请求资源

2)使用资源

2)释放资源

死锁是一组进程被阻塞的情况,因为每个进程都持有一个资源并等待某个其他进程获取的另一个资源。

考虑一个例子,当两列火车在同一轨道上相互靠近并且只有一条轨道时,一旦它们在彼此前面,没有一列列车可以移动。当有两个或多个进程持有某些资源并等待其他资源持有的资源时,在操作系统中会出现类似的情况。例如,在下图中,进程1保持资源1并等待由进程2获取的资源2,并且进程2正在等待资源1。

如果同时遵循四个条件,则可能出现死锁(必要条件)

相互排斥:一个或多个资源是不可共享的(一次只能使用一个进程)

保持和等待:进程至少保留一个资源并等待对于资源。

No Preemption:除非进程释放资源,否则无法从进程中获取资源。

循环等待:一组进程以循环形式相互等待。

处理死锁的方法

有三种方法可以处理死锁

1)死锁防止或避免:想法是不让系统进入死锁状态。

2)死锁检测和恢复:发生死锁,然后抢先处理它一旦发生。

3)一起忽略问题:如果死锁非常罕见,那就让它发生并重启系统。这是Windows和UNIX采用的方法。

Java死锁条件的一个简单示例

// Java program to illustrate Deadlock

// in multithreading.

class Util

{

// Util class to sleep a thread

static void sleep(long millis)

{

try

{

Thread.sleep(millis);

}

catch (InterruptedException e)

{

e.printStackTrace();

}

}

}

// This class is shared by both threads

class Shared

{

// first synchronized method

synchronized void test1(Shared s2)

{

System.out.println("test1-begin");

Util.sleep(1000);

// taking object lock of s2 enters

// into test2 method

s2.test2(this);

System.out.println("test1-end");

}

// second synchronized method

synchronized void test2(Shared s1)

{

System.out.println("test2-begin");

Util.sleep(1000);

// taking object lock of s1 enters

// into test1 method

s1.test1(this);

System.out.println("test2-end");

}

}

class Thread1 extends Thread

{

private Shared s1;

private Shared s2;

// constructor to initialize fields

public Thread1(Shared s1, Shared s2)

{

this.s1 = s1;

this.s2 = s2;

}

// run method to start a thread

@Override

public void run()

{

// taking object lock of s1 enters

// into test1 method

s1.test1(s2);

}

}

class Thread2 extends Thread

{

private Shared s1;

private Shared s2;

// constructor to initialize fields

public Thread2(Shared s1, Shared s2)

{

this.s1 = s1;

this.s2 = s2;

}

// run method to start a thread

@Override

public void run()

{

// taking object lock of s2

// enters into test2 method

s2.test2(s1);

}

}

public class GFG

{

public static void main(String[] args)

{

// creating one object

Shared s1 = new Shared();

// creating second object

Shared s2 = new Shared();

// creating first thread and starting it

Thread1 t1 = new Thread1(s1, s2);

t1.start();

// creating second thread and starting it

Thread2 t2 = new Thread2(s1, s2);

t2.start();

// sleeping main thread

Util.sleep(2000);

}

}

输出:

Output : test1-begin

test2-begin

不建议使用在线IDE运行上述程序。我们可以复制源代码并在我们的本地机器上运行它。我们可以看到它运行无限期,因为线程处于死锁状态并且不允许代码执行。现在让我们一步一步地看到那里发生的事情。

线程t1启动并通过获取s1的对象锁来调用test1方法。

线程t2启动并通过获取s2的对象锁来调用test2方法。

t1打印test1-begin,t2打印test-2 begin,两者都等待1秒,这样两个线程都可以启动,如果没有。

t1尝试获取s2的对象锁并调用方法test2,但因为它已被t2获取所以它等待它变为空闲。在获得s2锁定之前,它不会释放s1的锁定。

t2也是如此。它试图获取s1的对象锁并调用方法test1但它已被t1获取,因此它必须等到t1释放锁。在获得s1锁定之前,t2也不会释放s2的锁定。

现在,两个线程都处于等待状态,等待彼此释放锁定。现在有一种竞争条件,谁将首先释放锁定。

由于他们都没有准备好释放锁定,所以这就是Dead Lock条件。

当您运行此程序时,它将看起来像执行暂停。

检测Java死锁情况

我们还可以通过在cmd上运行此程序来检测死锁。我们必须收集线程转储。要收集的命令取决于操作系统类型。如果我们使用Windows和Java 8,命令是jcmd $ PID Thread.print

我们可以通过运行jps命令来获取PID。以上程序的线程转储如下:

5524:

2017-04-21 09:57:39

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.25-b02 mixed mode):

"DestroyJavaVM" #12 prio=5 os_prio=0 tid=0x0000000002690800 nid=0xba8 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Thread-1" #11 prio=5 os_prio=0 tid=0x0000000018bbf800 nid=0x12bc waiting for monitor entry [0x000000001937f000]

java.lang.Thread.State: BLOCKED (on object monitor)

at Shared.test1(GFG.java:15)

- waiting to lock (a Shared)

at Shared.test2(GFG.java:29)

- locked (a Shared)

at Thread2.run(GFG.java:68)

"Thread-0" #10 prio=5 os_prio=0 tid=0x0000000018bbc000 nid=0x1d8 waiting for monitor entry [0x000000001927f000]

java.lang.Thread.State: BLOCKED (on object monitor)

at Shared.test2(GFG.java:25)

- waiting to lock (a Shared)

at Shared.test1(GFG.java:19)

- locked (a Shared)

at Thread1.run(GFG.java:49)

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x000000001737d800 nid=0x1680 runnable [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x000000001732b800 nid=0x17b0 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x0000000017320800 nid=0x7b4 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x000000001731b000 nid=0x21b0 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000017319800 nid=0x1294 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000017318000 nid=0x1efc runnable [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000002781800 nid=0x5a0 in Object.wait() [0x000000001867f000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on (a java.lang.ref.ReferenceQueue$Lock)

at java.lang.ref.ReferenceQueue.remove(Unknown Source)

- locked (a java.lang.ref.ReferenceQueue$Lock)

at java.lang.ref.ReferenceQueue.remove(Unknown Source)

at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000000277a800 nid=0x15b4 in Object.wait() [0x000000001857f000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on (a java.lang.ref.Reference$Lock)

at java.lang.Object.wait(Unknown Source)

at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

- locked (a java.lang.ref.Reference$Lock)

"VM Thread" os_prio=2 tid=0x00000000172e6000 nid=0x1fec runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000026a6000 nid=0x21fc runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000026a7800 nid=0x2110 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000026a9000 nid=0xc54 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000026ab800 nid=0x704 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000018ba0800 nid=0x610 waiting on condition

JNI global references: 6

Found one Java-level deadlock:

=============================

"Thread-1":

waiting to lock monitor 0x0000000018bc1e88 (object 0x00000000d5d645a0, a Shared),

which is held by "Thread-0"

"Thread-0":

waiting to lock monitor 0x0000000002780e88 (object 0x00000000d5d645b0, a Shared),

which is held by "Thread-1"

Java stack information for the threads listed above:

===================================================

"Thread-1":

at Shared.test1(GFG.java:15)

- waiting to lock (a Shared)

at Shared.test2(GFG.java:29)

- locked (a Shared)

at Thread2.run(GFG.java:68)

"Thread-0":

at Shared.test2(GFG.java:25)

- waiting to lock (a Shared)

at Shared.test1(GFG.java:19)

- locked (a Shared)

at Thread1.run(GFG.java:49)

Found 1 deadlock.

我们可以看到有明确提到发现1死锁。尝试启动计算机时可能会显示相同的消息。

避免Java死锁情况

我们可以通过了解它的可能性来避免死锁状态。这是一个非常复杂的过程,不易捕捉。但是,如果我们尝试,我们可以避免这种情况。有一些方法可以避免这种情况。我们不能完全消除它的可能性,但我们可以减少。

避免嵌套锁:这是死锁的主要原因。Dead Lock主要发生在我们为多个线程提供锁定时。如果我们已经给了多个线程,请避免锁定多个线程。

避免不必要的锁:我们应该只锁定那些必需的成员。不必要地锁定会导致死锁。

使用线程连接:当一个线程等待其他线程完成时,将显示死锁条件。如果出现这种情况,我们可以使用Thread.join,您认为执行将花费最多时间。

重点:

如果线程正在等待彼此完成,则该条件称为死锁。

死锁条件是一种复杂的情况,仅在多线程的情况下才会发生。

死锁条件可能会在运行时破坏我们的代码并可能破坏业务逻辑。

我们应该尽可能地避免这种情况。

java线程中的死锁_Java多线程中的死锁 - Break易站相关推荐

  1. java线程休眠sleep函数_Java多线程中sleep()方法详解及面试题

    一. Java线程生命周期(五个阶段) 新建状态就绪状态运行状态阻塞状态死亡状态 如图 二.sleep方法 API中的解释 static voidsleep(long millis) 使当前正在执行的 ...

  2. java 线程的基本概念_Java多线程——基本概念

    线程和多线程 程序:是一段静态的代码,是应用软件执行的蓝本 进程:是程序的一次动态执行过程,它对应了从代码加载.执行至执行完毕的一个完整过程,这个过程也是进程本身从产生.发展至消亡的过程 线程:是比进 ...

  3. java 线程的基本概念_Java多线程——多线程的基本概念和使用

    一.进程和线程的基础知识 1.进程和线程的概念 进程:运行中的应用程序称为进程,拥有系统资源(cpu.内存) 线程:进程中的一段代码,一个进程中可以有多段代码.本身不拥有资源(共享所在进程的资源) 在 ...

  4. java线程看不进去_Java多线程和并发基础面试问答,看过后你不会后悔

    第一:Java多线程面试问题 1:过程和线程之间有什么不合? 一个过程是一个自力(self contained)的运行情况,它可以被看作一个法度榜样或者一个应用.而线程是在过程中履行的一个义务.Jav ...

  5. java线程的内存模型_java多线程内存模型

    java多线程内存模型: 可见性 要实现共享变量的可见性,必须保证两点: 1.线程修改后的共享变量能够及时从工作内存刷新到主内存中: 2.其他线程能够及时把共享变量的最新值从主内存更新到自己的工作内存 ...

  6. java线程的实现方法_Java多线程的四种实现方式

    1.Java多线程实现的方式有四种: 1.继承Thread类,重写run方法 2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的targ ...

  7. java 线程同步的方法_Java多线程同步方法

    Java多线程同步方法 package com.wkcto.intrinsiclock; /** * synchronized同步实例方法 * 把整个方法体作为同步代码块 * 默认的锁对象是this对 ...

  8. java线程打水问题_Java 多线程 wait() 虚假唤醒问题

    本文分享 wait()  的虚假唤醒(Spurious Wakeups)问题,会说明什么是虚假唤醒,以及如何解决. 先看一下相关的 java doc: java doc 说由于中断和虚假唤醒可能会发生 ...

  9. java 构造函数 单例_Java中的私有构造函数和单例类 - Break易站

    Java 构造函数 Java中的私有构造函数和单例类 我们首先分析下面的问题: 我们可以有私人构造函数吗? 正如你可以很容易猜到的,就像我们可以为构造函数提供访问说明符一样.如果它是私人的,那么它只能 ...

最新文章

  1. 【转】CEC文件详解
  2. Educational Codeforces Round 75 (Rated for Div. 2)
  3. go 监测tcp 连接断开_TCP三次握手和四次挥手以及11种状态
  4. STM32CubeMX使用(一)之实现点灯点灯
  5. 32位的tetview and medit 在64bit的linux运行,有很多32bit的库没有安装,错误不断之解决办法。
  6. 使用计算机传真,使用计算机发送和接收传真
  7. 软工网络15团队作业8——Beta阶段冲刺合集
  8. win10 win11黑屏引导转圈开机时间过长
  9. Java实现细菌觅食算法_细菌觅食算法的matlab代码
  10. 广播电视相关信息系统 安全等级保护基本要求
  11. 大数据上传-GB/T 32960测试开发实践
  12. 微信小程序:九宫格抽奖
  13. mysql全套理解以及在php中的应用
  14. Spark中repartition和coalesce的用法
  15. ubuntu系统下THETA S 全景相机 通过ROS导出图像
  16. android动画结束保持,【Android】 动画
  17. JavaFX鼠标移入后改变样式
  18. php 静态 动态 cdn 加速,一次鸡肋(能用CDN代替)的DUX主题静态文件优化加速的日志...
  19. 双网卡电脑如何设置同时上两个不同的网络
  20. 项目管理大会演讲PPT

热门文章

  1. 深入分析之Cluster层
  2. C++11_范围for循环
  3. 异步并发利器:实际项目中使用CompletionService提升系统性能的一次实践
  4. idea2020代码el组件红色波浪线_Android实现炫酷的ViewPager3D组件
  5. percona-distribution-mysql-pxc
  6. Vue computed 带参数
  7. Federated Machine Learning: Concept and Applications
  8. 基本操作1-本地仓库连接远程仓库
  9. [数据库笔记]规范化(Normalization)-把数据冗余降下来
  10. iOS重写和成员变量访问权限