什么是死锁
写线程同步的时候,我在最后举了个例子:同步代码块相当于是厕所(恶臭的好例子),一个人进去,另外一个人只有等他出来才能进去。
假如有这样一种情况,手纸是另一个同步代码块,甲拿了纸,想进厕所,乙在厕所,没带纸想用纸。哦吼,这就是个死锁了。
解决死锁的基本思路
还是上面的那个例子,让占有手纸资源的线程先释放手纸资源,然后占据厕所资源的线程就可以拿到手纸,等有厕所资源,手纸资源的线程执行完毕后会释放这两个资源,然后另一个资源就可以去上了。。。
/*死锁模拟-哲学家吃饭的问题
解决方法 :将锁的粒度加粗,只用一个对象锁
很难涉及到,加了锁就要小心死锁的问题*/
public class TestDeadLock implements Runnable
{
public int flag = 1;
static Object o1 = new Object(), o2 = new Object();
public void run()
{
System.out.println("flag=" + flag);
if (flag == 1)
{
synchronized (o1)
{
try
{
Thread.sleep(500);
}
catch (Exception e)
{
e.printStackTrace();
}
try
{
o1.wait(); //o1对象锁释放自己的钥匙。
}
catch (InterruptedException e)
{
e.printStackTrace();
}
synchronized (o2)
{
System.out.println("1");
}
}
}
if (flag == 0)
{
synchronized (o2)
{
try
{
Thread.sleep(500);
}
catch (Exception e)
{
e.printStackTrace();
}
synchronized (o1)
{
System.out.println("0");
o1.notify(); //o1对象锁在释放之前唤醒等待池中的线程继续执行。所以唤醒动作必须放到里面。
}
}
}
}
public static void main(String[] args)
{
TestDeadLock td1 = new TestDeadLock();
TestDeadLock td2 = new TestDeadLock();
td1.flag = 1;
td2.flag = 0;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
t1.start();
t2.start();
}
}
其中有一些点要说明
wait()和notify()的调用
- 当前线程在调用wait()/notify()时,必须拥有该对象的同步锁
- 要在拥有该同步锁的同步块中调用
线程通信(生产者消费者问题)
现在有一筐窝窝头(栈),生产者往框里放窝窝头,消费者从框里取窝窝头,如果框里没有窝窝头了,消费者就不能取窝窝头,假如这个框能装6个窝窝头,那么装满了后生产者也就停止生产窝窝头,每个消费者吃两个窝窝头,如何解决消费者和生产者之间的通信问题呢?很简单,加一个旗标标值有没有窝窝头就好了。
package thread;
/**
* @author Mike
* @use 生产者消费者问题
*/
public class ProducerConsumer extends Thread {
static SyncStack ss; // 框对象创建
static Thread pro1;
static Thread cus1;
static Thread cus2;
static Thread cus3;
static Thread cus4;
static Thread cus5;
static Thread cus6;
public static void main(String[] args)
{
ss = new SyncStack(); // 框对象创建
Producer p1 = new Producer(ss);
Consumer c1 = new Consumer(ss);
Consumer c2 = new Consumer(ss);
Consumer c3 = new Consumer(ss);
Consumer c4 = new Consumer(ss);
Consumer c5 = new Consumer(ss);
Consumer c6 = new Consumer(ss);
pro1 = new Thread(p1, "厨师1");
cus1 = new Thread(c1, "客户1");
cus2 = new Thread(c2, "客户2");
cus3 = new Thread(c3, "客户3");
cus4 = new Thread(c4, "客户4");
cus5 = new Thread(c5, "客户5");
cus6 = new Thread(c6, "客户6");
pro1.start();
cus1.start();
cus2.start();
cus3.start();
cus4.start();
cus5.start();
cus6.start();
new ProducerConsumer().start();
// 启动厨师和客户线程。
/*
* p[0].start(); c[0].start(); c[1].start();
*/
}
public void run()
{
while (true)
{
if ((!cus1.isAlive()) && (!cus2.isAlive()) && (!cus3.isAlive())
&& (!cus4.isAlive()) && (!cus5.isAlive())
&& (!cus6.isAlive()))
{
ss.flag = false;
System.out.println("下班了!");
break;
}
try
{
Thread.sleep(200);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
/**
* 类说明:馒头类
*/
class WoTou
{
int id;
WoTou(int id)
{
this.id = id;
}
public String toString()
{
return "WoTou : " + id;
}
}
/**
* 类说明:框类
*/
class SyncStack
{
int index = 0;
WoTou[] arrWT = new WoTou[6]; // 框,只能放6个馒头
boolean flag = true;
/**
* 方法说明:往框里面放
*
* @param wt
*/
public void push(WoTou wt)
{
while (index >= arrWT.length)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
arrWT[index] = wt;
index++;
System.out.println(Thread.currentThread().getName() + "生产了:" + wt);
this.notifyAll();
}
/**
* 方法说明:从框里面取
*
* @return
*/
public WoTou pop()
{
while (index <= 0)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
index--;
WoTou woTou = arrWT[index];
System.out.println(Thread.currentThread().getName() + "消费了: " + woTou);
this.notifyAll();
return woTou;
}
}
/**
* 类说明:生产者生产馒头
*/
class Producer implements Runnable
{
SyncStack ss = null;
static int num = 0;
Producer(SyncStack ss)
{
this.ss = ss;
}
public void run()
{
while (ss.flag)
{
synchronized (ss)
{
WoTou wt = new WoTou(num++);
ss.push(wt);
try
{
Thread.sleep((int) (Math.random() * 2000));
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
/**
* 类说明:消费者
*/
class Consumer implements Runnable
{
SyncStack ss = null;
int num = 0;
Consumer(SyncStack ss)
{
this.ss = ss;
}
public void run()
{
while (num < 2)
{
synchronized (ss)
{
ss.pop();
num = num + 1;
try
{
Thread.sleep((int) (Math.random() * 2000));
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
线程的学习就先到这里,其实应该还有线程池等其他东西,但现阶段没有学到先放一放,毕竟光线程就是很厚一本书了,教材上才用了一章来讲,等基础知识在扎实点在去看线程的书,然后再总结博客吧。