
正文
java多线程死锁代码,java多线程锁synchronized
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
JAVA中如何去避免多线程产生的死锁
Java线程死锁需要如何解决,这个问题一直在我们不断的使用中需要只有不断的关键。不幸的是,使用上锁会带来其他问题。让我们来看一些常见问题以及相应的解决方法: Java线程死锁 Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。 假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。这只是人为设计的例子,但尽管在运行时很难探测到,这类情况却时常发生。虽然要探测或推敲各种情况是非常困难的,但只要按照下面几条规则去设计系统,就能够避免Java线程死锁问题: 让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。 将多个锁组成一组并放到同一个锁下。前面Java线程死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。 将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判断是否整个银器集合中的对象锁都可获得。如果是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。 最重要的是,在编写代码前认真仔细地设计整个系统。多线程是困难的,在开始编程之前详细设计系统能够帮助你避免难以发现Java线程死锁的问题。 Volatile 变量,volatile 关键字是 Java 语言为优化编译器设计的。以下面的代码为例: 1.class VolatileTest {
2.public void foo() {
3.boolean flag = false;
4.if(flag) {
5.//this could happen
6.}
7.}
8.} 一个优化的编译器可能会判断出if部分的语句永远不会被执行,就根本不会编译这部分的代码。如果这个类被多线程访问, flag被前面某个线程设置之后,在它被if语句测试之前,可以被其他线程重新设置。用volatile关键字来声明变量,就可以告诉编译器在编译的时候,不需要通过预测变量值来优化这部分的代码。 无法访问的Java线程死锁有时候虽然获取对象锁没有问题,线程依然有可能进入阻塞状态。在 Java 编程中IO就是这类问题最好的例子。当线程因为对象内的IO调用而阻塞时,此对象应当仍能被其他线程访问。该对象通常有责任取消这个阻塞的IO操作。造成阻塞调用的线程常常会令同步任务失败。如果该对象的其他方法也是同步的,当线程被阻塞时,此对象也就相当于被冷冻住了。 其他的线程由于不能获得对象的Java线程死锁,就不能给此对象发消息(例如,取消 IO 操作)。必须确保不在同步代码中包含那些阻塞调用,或确认在一个用同步阻塞代码的对象中存在非同步方法。尽管这种方法需要花费一些注意力来保证结果代码安全运行,但它允许在拥有对象的线程发生阻塞后,该对象仍能够响应其他线程。 编辑推荐: 1. Java多线程优化之偏向锁原理分析 2. Java多线程实现异步调用的方法 3. 使用Java多线程机制实现下载的方法介绍
相关问答
Q1: java线程同步,死锁题目求代码
可以建一个类作为锁 然后两个线程
......
synchronized (seed) {
......
seed.notifyAll();
try {
seed.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
以下仅供参考
package com.kidd.test.zhidao;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
* Hello world!
*
*/
public class Main {
public static void main(String[] args) {
ListInteger pais = new ArrayList();
ListInteger list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
pais.addAll(list);
pais.addAll(list);
Seed seed = new Seed();
Person person1 = new Person(0, "玩家A", seed);
Person person2 = new Person(1, "玩家B", seed);
fapai(pais, person1, person2);
Thread thread1 = new Thread(person1);
Thread thread2 = new Thread(person2);
thread1.start();
thread2.start();
}
private static void fapai(ListInteger pais, Person person1, Person person2) {
int i = 1;
int index;
Random random = new Random();
while (pais.size() 0) {
switch (i) {
case 1: {
index = pais.remove(random.nextInt(pais.size()));
person1.addPai(index);
i++;
break;
}
case 2: {
index = pais.remove(random.nextInt(pais.size()));
person2.addPai(index);
i = 1;
break;
}
}
}
Arrays.sort(person1.getPais());
Arrays.sort(person2.getPais());
System.out.println("牌发完了.");
System.out.println(person1.getName() + ":我的牌组是" + Arrays.toString(person1.getPais()));
System.out.println(person2.getName() + ":我的牌组是" + Arrays.toString(person1.getPais()));
}
}
class Person implements Runnable {
private int index;
private int n;
private int[] pais = new int[13];
private String name;
private Seed seed;
public Person(int index, String name, Seed seed) {
this.index = index;
this.name = name;
this.seed = seed;
}
public Seed getSeed() {
return seed;
}
public String getName() {
return name;
}
public int[] getPais() {
return pais;
}
public void setPais(int[] pais) {
this.pais = pais;
}
public int getN() {
return n;
}
public int addPai(int i) {
pais[n] = i;
n++;
return n;
}
@Override
public void run() {
synchronized (seed) {
while (!seed.isEnd() n != 0) {
/**
* 判断是否轮到该玩家出牌,否则唤醒其他线程并且该线程睡眠.
*/
while (seed.getIndex() != this.index) {
seed.notifyAll();
try {
seed.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 线程唤醒后,检查中断标记
*/
if (seed.isEnd()) {
break;
}
/**
* 判断出牌是否成功,成功就轮到下一个玩家.否则重置牌,并轮到下一个玩家.
*/
if (chupai()) {
seed.nextIndex();
} else {
seed.nextIndex();
seed.setPai(0);
}
}
/**
* 设置线程退出的标记
*/
seed.setEnd(true);
if (n == 0) {
System.out.println(name + ":牌出完了,我赢了.");
} else {
System.out.println(name + ":我输了.还剩" + n + "张牌.牌组是:" + Arrays.toString(pais));
}
/**
* 退出前把其他线程唤醒.
*/
seed.notifyAll();
}
}
private boolean chupai() {
int a;
for (int i = 0, k = pais.length; i k; i++) {
a = pais[i];
if (a != 0 a seed.getPai()) {
System.out.println(name + ":大你。我出" + pais[i]);
seed.setPai(a);
pais[i] = 0;
n--;
return true;
}
}
System.out.println(name + "不出牌:(无牌可出).");
return false;
}
}
class Seed {
private boolean end;
private int pai;
private int index;
public boolean isEnd() {
return end;
}
public int getPai() {
return pai;
}
public void setPai(int pai) {
this.pai = pai;
}
public int getIndex() {
return index;
}
public void nextIndex() {
this.index++;
if (this.index == 2) {
this.index = 0;
}
}
public void setEnd(boolean end) {
this.end = end;
}
}
Q2: JAVA中,线程死锁是什么意思
一. 什么是线程
在谈到线程死锁的时候,我们首先必须了解什么是Java线程。一个程序的进程会包含多个线程,一个线程就是运行在一个进程中的一个逻辑流。多线程允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。
线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信较进程简单。笔者的经验是编写多线程序,必须注意每个线程是否干扰了其他线程的工作。每个进程开始生命周期时都是单一线程,称为“主线程”,在某一时刻主线程会创建一个对等线程。如果主线程停滞则系统就会切换到其对等线程。和一个进程相关的线程此时会组成一个对等线程池,一个线程可以杀死其任意对等线程。
因为每个线程都能读写相同的共享数据。这样就带来了新的麻烦:由于数据共享会带来同步问题,进而会导致死锁的产生。
二. 死锁的机制
由多线程带来的性能改善是以可靠性为代价的,主要是因为有可能产生线程死锁。死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不能正常运行。简单的说就是:线程死锁时,第一个线程等待第二个线程释放资源,而同时第二个线程又在等待第一个线程释放资源。这里举一个通俗的例子:如在人行道上两个人迎面相遇,为了给对方让道,两人同时向一侧迈出一步,双方无法通过,又同时向另一侧迈出一步,这样还是无法通过。假设这种情况一直持续下去,这样就会发生死锁现象。
导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。
Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。下面笔者分析死锁的两个过程“上锁”和“锁死” 。
(1) 上锁
许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态,就需要同步机制。因此大多数应用程序要求线程互相通信来同步它们的动作,在 Java 程序中最简单实现同步的方法就是上锁。在 Java 编程中,所有的对象都有锁。线程可以使用 synchronized 关键字来获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。
为了防止同时访问共享资源,线程在使用资源的前后可以给该资源上锁和开锁。给共享变量上锁就使得 Java 线程能够快速方便地通信和同步。某个线程若给一个对象上了锁,就可以知道没有其他线程能够访问该对象。即使在抢占式模型中,其他线程也不能够访问此对象,直到上锁的线程被唤醒、完成工作并开锁。那些试图访问一个上锁对象的线程通常会进入睡眠状态,直到上锁的线程开锁。一旦锁被打开,这些睡眠进程就会被唤醒并移到准备就绪队列中。
(2)锁死
如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源,系统中没有饿死和死锁的线程。当多个并发的线程分别试图同时占有两个锁时,会出现加锁冲突的情形。如果一个线程占有了另一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
在编写多线程代码时,笔者认为死锁是最难处理的问题之一。因为死锁可能在最意想不到的地方发生,所以查找和修正它既费时又费力。例如,常见的例子如下面这段程序。
public int sumArrays(int[] a1, int[] a2) ...{ int value = 0; int size = a1.length; if (size == a2.length) ...{ synchronized(a1) ...{ //1 synchronized(a2) ...{ //2 for (int i=0; isize; i++) value += a1[i] + a2[i]; } } } return value; }
这段代码在求和操作中访问两个数组对象之前锁定了这两个数组对象。它形式简短,编写也适合所要执行的任务;但不幸的是,它有一个潜在的问题。这个问题就是它埋下了死锁的种子。
没有完结,请楼主看下面的网址。
java多线程死锁代码的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java多线程锁synchronized、java多线程死锁代码的信息别忘了在本站进行查找喔。






