
在几乎所有编程语言中,都很难重现由多线程引起的错误. 程序的死锁或其他多线程错误可能仅在某些特殊情况下或在不同的VM上出现. 运行同一程序时,错误的行为有所不同. 因此,在编写多线程程序时,预先识别并防止可能的错误特别重要. 无论是客户端多线程Java程序,还是服务器端多线程Java程序,最常见的多线程问题都包括死锁,隐式死锁和数据争用.
如何避免Java线程死锁的悲剧如何解决Java线程死锁的问题,这个问题一直是我们持续使用中唯一需要解决的关键. 不幸的是,使用锁会导致其他问题. 让我们看一些常见的问题和相应的解决方案:
死锁
死锁是一种情况,其中多个线程同时被阻塞,并且其中一个或全部正在等待释放资源. 由于线程被无限期阻塞,因此程序无法正常终止.
死锁的根本原因是不恰当地使用了“ synchronized”关键字来管理对特定对象的线程访问. “ synchronized”关键字的目的是确保一次只允许一个线程执行特定的代码块. 因此,被允许执行的线程必须首先具有对变量或对象的独占访问权. 当一个线程访问一个对象时,该线程将锁定该对象,并且该锁定导致也希望访问同一对象的其他线程被阻塞,直到第一个线程释放它添加到该对象的锁为止.
因此,当使用“ synchronized”关键字时,两个线程很容易彼此等待以进行操作. 代码一是死锁的简单示例.
1class Deadlocker {
2int field_1;
3私有对象锁_1 =新int [1];
4int field_2;
5个私有对象lock_2 = new int [1];
6
7public void method1(整数值){
8个“已同步”(锁定_1){
9个“已同步”(锁定_2){
10 field_1 = 0; field_2 = 0;
11}
12}
13}
14
15public void method2(int值){
16个“已同步”(锁定_2){

17个“已同步”(锁定_1){
18 field_1 = 0; field_2 = 0;
19}
20}
21}
22}
23
Java代码
24class Deadlocker {
25int field_1;
26private对象lock_1 = new int [1];
27int field_2;
28private对象lock_2 = new int [1];
29
30 public void method1(整数值){
31个“已同步”(锁定_1){
32个“已同步”(锁定_2){
33 field_1 = 0; field_2 = 0;
34}
35}
36}
37

38public void method2(int值){
39个“已同步”(锁定_2){
40个“已同步”(锁定_1){
41 field_1 = 0; field_2 = 0;
42}
43}
44}
45}
参考代码一,请考虑以下过程:
◆一个线程(ThreadA)调用method1().
◆ThreadA在lock_1上同步,但允许抢先执行.
◆另一个线程(ThreadB)开始执行.
ThreadB调用method2().
◆ThreadB获取lock_2并继续执行,尝试获取lock_1. 但是ThreadB无法获得lock_1,因为ThreadA拥有lock_1.
◆现在,ThreadB被阻止,因为它正在等待ThreadA释放lock_1.
◆现在轮到ThreadA继续执行了. ThreadA试图获取lock_2,但是失败了,因为lock_2已经被ThreadB占用.
ThreadA和ThreadB被阻止,程序死锁.
当然,大多数死锁并不是那么明显,您需要仔细分析代码才能看到,特别是对于大型多线程程序. 好的线程分析工具,例如JProbe Threadalyzer,可以分析死锁并指出导致问题的代码的位置.
Java线程死锁是一个经典的多线程问题,因为不同的线程正在等待根本无法释放的锁,从而导致所有工作无法完成. 假设有两个线程,代表两个饥饿的人,他们必须共享刀叉并轮流吃饭. 他们俩都需要获得两个锁: 共享刀和共享叉锁.
假设线程“ A”收到了刀,线程“ B”收到了叉子. 线程“ A”将进入阻塞状态以等待叉,而线程“ B”将阻塞以等待“ A”拥有的刀. 这只是人为设计的一个例子,但是尽管在运行时很难检测到,但是这种情况经常发生. 尽管很难检测或检查各种情况,但是只要按照以下规则设计系统,就可以避免Java线程死锁问题:
让所有线程以相同的顺序获取一组锁. 这种方法消除了X和Y所有者分别等待对方资源的问题.
将多个锁组合在一起,并将它们置于同一锁下. 在前面的Java线程死锁示例中,您可以为银器对象创建锁. 因此,必须在获得刀或叉之前获得银器的锁.

标记不会被变量阻塞的可用资源. 当线程获取银器对象的锁时,它可以通过检查变量来检查整个银器集合中的对象锁是否可用. 如果是,则可以获取相关的锁,否则java多线程避免死锁,有必要释放银器锁并稍后再试.
最重要的是在编写代码之前仔细设计整个系统. 多线程很难. 在开始编程之前详细设计系统可以帮助您避免发现Java线程死锁的问题. 让我们再举一个例子
Volatile变量,volatile关键字是由Java语言设计的,用于优化编译器. 以下面的代码为例:
classVolatileTest {
public void foo() {
boolean flag = false;
if(flag) {
//this could happen
优化的编译器可以确定该语句的if部分将永远不会执行,并且根本不会编译该部分代码. 如果该类由多个线程访问,则在由前一个线程设置该标志之后,可以通过其他线程将其重置,然后再通过if语句对其进行测试. 使用volatile关键字声明变量,可以告诉编译器在编译时不需要预测变量的值即可优化代码的这一部分.
无法访问的Java线程死锁有时,尽管获取对象锁没有问题,但是线程仍可能进入阻塞状态. IO是Java编程中此类问题的最佳示例. 当某个线程被对象中的IO调用阻塞时,该对象仍应可由其他线程访问. 该对象通常负责取消此阻塞的IO操作. 导致阻塞调用的线程通常使同步任务失败. 如果该对象的其他方法也同步,则程被阻塞时,该对象也会被冻结.
由于无法获取对象的Java线程的死锁,其他线程无法向该对象发送消息(例如,取消IO操作). 您必须确保不将那些阻塞调用包括在同步代码中,或者确保使用同步阻塞代码的对象中存在异步方法. 尽管此方法需要注意以确保生成的代码安全运行,但它允许对象在拥有对象块的线程之后响应其他线程.
隐性死锁
隐式死锁是由非标准的编程方法引起的,但是可能不会在每次测试运行时发生. 因此,在正式发布应用程序之前,可能不会发现某些隐藏的死锁,因此,它比普通的死锁更为有害. 下面介绍导致隐式死锁的两种情况: 锁定顺序以及拥有和等待.
锁定顺序
当多个并发线程尝试同时占用两个锁时,锁顺序将发生冲突. 如果一个线程占用了另一个线程所需的锁,则可能存在死锁. 考虑以下情况,两个线程ThreadA和ThreadB需要同时具有两个锁lock_1和lock_2,其锁定过程可能如下:
◆ThreadA获得lock_1;
◆线程A被抢占,并且VM调度程序转到线程B;
◆ThreadB获得lock_2;
◆抢占ThreadB,并且VM调度程序转到ThreadA;
ThreadA尝试获取lock_2,但是lock_2被ThreadB占用,因此ThreadA被阻止;
◆调度程序转到ThreadB;
◆ThreadB尝试获取lock_1,但是lock_1被ThreadA占用,因此ThreadB被阻止;
◆ThreadA和ThreadB死锁.
必须注意,在不更改代码的情况下,有时上述死锁过程不会出现,并且VM调度程序可能允许线程之一获得两个锁,lock_1和lock_2,即线程获取两个锁定过程未中断. 在这种情况下,传统的死锁检测很难确定错误.
拥有并等待
如果一个线程获取了一个锁并等待来自另一个线程的通知,则可能会发生另一个隐式死锁. 考虑代码二.

47 public class queue {
48 static java.lang.Object queueLock_;
49 Producer producer_;
50 Consumer consumer_;
51
52 public class Producer {
53 void produce() {
54 while (!done) {
55 “synchronized” (queueLock_) {
56 produceItemAndAddItToQueue();
57 “synchronized” (consumer_) {
58 consumer_.notify();
59 }
60 }
61 }
62 }
63
64 public class Consumer {
65 consume() {
66 while (!done) {
67 “synchronized” (queueLock_) {
68 “synchronized” (consumer_) {
69 consumer_.wait();
70 }
71 removeItemFromQueueAndProcessIt();
72 }
73 }
74 }
75 }
76 }
77 }
78
Java代码
79 public class queue {
80 static java.lang.Object queueLock_;
81 Producer producer_;
82 Consumer consumer_;
83
84 public class Producer {
85 void produce() {
86 while (!done) {
87 “synchronized” (queueLock_) {
88 produceItemAndAddItToQueue();
89 “synchronized” (consumer_) {
90 consumer_.notify();
91 }
92 }
93 }
94 }
95
96 public class Consumer {
97 consume() {
98 while (!done) {
99 “synchronized” (queueLock_) {
100 “synchronized” (consumer_) {
101 consumer_.wait();
102 }
103 removeItemFromQueueAndProcessIt();
104 }
105 }
106 }
107 }
108 }
109 }
在代码2中,生产者将新内容添加到队列后通知消费者,以便其可以处理新内容. 问题在于,消费者可能将锁添加到队列中,阻止生产者访问队列,甚至在消费者等待生产者通知时保持锁定. 这样,由于生产者无法将新内容添加到队列中java多线程避免死锁,但是消费者正在等待生产者添加新的内容通知,从而导致死锁.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-227888-1.html
第二
这是赤裸裸的侵略
前9个月的CPI是1
我的6是9