前言
本次学习内容包括:线程死锁问题
、生产者和消费者问题
一、线程死锁
简单来说就是两个或多个线程各自占据资源,不释放,又想去获取别的线程占据的资源,从而导致每个线程都运行不下去,造成阻塞。
造成死锁的条件:
1、互斥条件:多个线程竞争同一个资源,并且每个线程在持有一个资源的同时还想要获取其他线程持有的资源。这种情况下,如果没有正确的资源分配顺序,就可能导致死锁。
2、请求和保持:一个线程在持有一个资源的同时,又请求其他线程所持有的资源。如果多个线程都采取这种策略,就可能形成死锁。
3、不可剥夺条件:线程获取到的资源在一段时间内不能被强制剥夺。如果一个线程在持有某个资源时,其他线程无法强制剥夺该资源,就可能导致死锁。
4、循环等待:多个线程形成循环等待资源的关系,
例子:
public class Test implements Runnable {
public int flag = 1;
static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "的flag=" + flag);
/*
* 运行程序后发现程序执行到这里打印出flag以后就再也不往下执行后面的if语句了
* 程序也就死在了这里,既不往下执行也不退出
*/
/* 这是flag=1这个线程 */
if (flag == 1) {
synchronized (o1) {
/* 使用synchronized关键字把对象01锁定了 */
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
/*
* 前面已经锁住了对象o1,只要再能锁住o2,那么就能执行打印出1的操作了
* 可是这里无法锁定对象o2,因为在另外一个flag=0这个线程里面已经把对象o1给锁住了
* 尽管锁住o2这个对象的线程会每隔500毫秒睡眠一次,可是在睡眠的时候仍然是锁住o2不放的
*/
System.out.println("1");
}
}
}
/*
* 这里的两个if语句都将无法执行,因为已经造成了线程死锁的问题
* flag=1这个线程在等待flag=0这个线程把对象o2的锁解开,
* 而flag=0这个线程也在等待flag=1这个线程把对象o1的锁解开
* 然而这两个线程都不愿意解开锁住的对象,所以就造成了线程死锁的问题
*/
/* 这是flag=0这个线程 */
if (flag == 0) {
synchronized (o2) {
/* 这里先使用synchronized锁住对象o2 */
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
/*
* 前面已经锁住了对象o2,只要再能锁住o1,那么就能执行打印出0的操作了 可是这里无法锁定对象o1,因为在另外一个flag=1这个线程里面已经把对象o1给锁住了 尽管锁住o1这个对象的线程会每隔500毫秒睡眠一次,可是在睡眠的时候仍然是锁住o1不放的
*/
System.out.println("0");
}
}
}
}
}
----------------------------------------------------------------------------------------------
public class MyThread {
public static void main(String[] args) {
Test td1 = new Test();
Test td2 = new Test();
td1.flag = 1;
td2.flag = 0;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
t1.setName("线程td1");
t2.setName("线程td2");
t1.start();
t2.start();
}
}
小提示:解决线程死锁的问题最好只锁定一个对象,不要同时锁定两个对象
二、生产者与消费者
生产者与消费者问题是Java多线程
的一个典型问题,其特点是:
生产者消费者同时使用一块缓冲区,生产者生产商品放入缓冲区,消费者从缓冲区取出商品。我们需要保证的是,当缓冲区满时,生产者不可生产商品;当缓冲区为空时,消费者不可取出商品。
这其中涉及到线程同步的问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
要解决的最主要的问题是当多个线程访问资源时,如何保证资源的完整性。我们会采用对线程加锁的方式来解决这个问题。
例子:
//支持多线程同步操作的堆栈的实现
public class Sync {
private int index = 0;
private char []data = new char[6];
public synchronized void push(char c){
if(index == data.length){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
data[index] = c;
index++;
}
public synchronized char pop(){
if(index ==0){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
index--;
return data[index];
}
}
-----------------------------------------------------------------------------------------------
public class Prod implements Runnable{
Sync stack;
public Prod(Sync s){
stack = s;
}
public void run(){
for(int i=0; i<20; i++){
char c =(char)(Math.random()*26+'A');
stack.push(c);
System.out.println("produced:"+c);
try{
Thread.sleep((int)(Math.random()*1000));
}catch(InterruptedException e){
}
}
}
}
-----------------------------------------------------------------------------------------------
public class Con implements Runnable{
Sync stack;
public Con(Sync s){
stack = s;
}
public void run(){
for(int i=0;i<20;i++){
char c = stack.pop();
System.out.println("消费:"+c);
try{
Thread.sleep((int)(Math.random()*1000));
}catch(InterruptedException e){
}
}
}
}
-----------------------------------------------------------------------------------------------
public class Test {
public static void main(String[] args) {
Sync stack = new Sync();
Runnable p=new Prod(stack);
Runnable c = new Con(stack);
Thread p1 = new Thread(p);
Thread c1 = new Thread(c);
p1.start();
c1.start();
}
}
结果
总结
前面两篇文章我们学习了多线程的线程创建、执行、对线程的控制方法,线程的优先级,如何实现线程的同步以及如何解决线程死锁,还学习了一个线程的典型应用,生产者与消费者问题。