多线程小结(二)
上次的多线程还没有写完,今天再把后面的给补充上吧
上一篇博客中写到了线程同步的问题,今天开始先介绍一种由于同步产生的问题-死锁
1.死锁
死锁是由同步问题产生的,当同步中嵌套同步,并且使用不相同的锁的时候,就容易产生死锁的现象。
下面的代码就演示了死锁的现象:
class Ticket implements Runnable
{
private int tick = 1000;
Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(obj) //同步代码块使用obj锁
{
show(); //同步函数show()
}
}
}
else
while(true)
show();
}
public synchronized void show() //同步函数show使用this锁
{
synchronized(obj) //函数中嵌套的同步代码块
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
}
}
同步虽然解决了多线程的安全问题,但有多个同步结构是却容易产生死锁
。
死锁会导致程序中断执行,所以在写代码时要避免死锁的发生。
2.等待与唤醒
多线程的程序中,经常会出现让一个线程等待,然后其他线程来执行,然会再唤醒等待中的线程的情况
下面先以生产者消费者的例子来介绍一下基本的等待与唤醒的应用。
<span style="font-size:14px;">class Resource //定义一个资源类
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name) //给资源赋值,set()函数供生产者线程使用
{
/*
当有多个生产者和消费者时,有可能出现当一个生产者判断完标记,标记为真,进入等待状态
此时另一个等待的生产者被唤醒,执行完了生产动作,将标记置为真了
所以当线程被唤醒时必须需要再次判断标记,看资源是否为空
*/
while(flag)
try{this.wait();}catch(Exception e){}
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
/*
在有多个线程的时候,只用notify容易出现只唤醒本方线程,导致所有线程进入等待状态的情况
所以需要使用notifyAll唤醒所有线程
*/
this.notifyAll();
}
public synchronized void out() //输出资源,out()函数供消费者线程使用
{
while(!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
this.notifyAll();
}
}
//生产者线程定义
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("+商品+");
}
}
}
//消费者线程
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
//定义多个生产者与消费者线程
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}</span>
此例子需要注意的地方就是当有多个生产者与消费者线程时,需要循环判断标记,防止重复生成
使用notifyAll函数唤醒所有线程,防止所有线程都进入等待状态。
JDK1.5 中提供了多线程的升级解决方案。
Lock接口:替代了Synchronized,提供了以下方法:
lock() 加锁
unlock() 解锁
newCondition() 获取Condition对象
Condition类:替代了Object wait notify notifyAll ,提供了以下方法:
await(); //进入等待
signal(); //唤醒一个线程
signalAll(); //唤醒所有线程
<span style="font-size:14px;">import java.util.concurrent.locks.*;
class ProducerConsumerDemo2
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock(); <span style="color:#FF0000;">//定义一个锁</span>
<span style="color:#FF0000;">//获取两个Condition对象,一个对应生产者,一个对应消费者</span>
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock(); <span style="color:#FF0000;"> //加锁</span>
try
{
while(flag)
condition_pro.await(); <span style="color:#FF0000;">//生产者进程进入等待状态</span>
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
condition_con.signal(); <span style="color:#FF0000;">//唤醒消费者进程,
//Condition的signal()函数只能唤醒使用对应的Condition对象进入等待的线程</span>
} <span style="color:#FF0000;">//这样就可以只唤醒对方的线程</span>
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品+");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}</span>