【等待唤醒机制】
涉及的方法:
1.wait():让线程处于冻结状态,被wait的线程会被存储到线程池中
2.notify();唤醒线程池中的一个线程(任意的线程)
3.notifyAll();唤醒线程池中所有的线程
这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法
必须要明确到底操作的是哪个锁上的线程
即:调用的时候,锁.wait();...
ps:这些方法都是在Object类中
【生产者消费者例子】
生产者消费者模式减少资源的浪费
代码例子:
/**
资源
*/
class Resource{
private String name;
private int count =1;
private bolean flag=false;
public synchronized void set(String name){
if(flag)
try{
this.wait();
}catch(InterruptedException e){
}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
notify();
}
public synchronized void out(){
if(!flag){
try{
this.wait();
}catch(InterruptedException e){
}
}
System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
flag=false;
notify();
}
}
/**
生产者
*/
class Producer implements Runnable{
private Resource r;
Producer(Resource r){
this.r=r;
}
pulic void run(){
while(true){
r.set("烤鸭");
}
}
}
/**
消费者
*/
class Consumer implements Runnable{
private Resource r;
Consumer (Resource r){
this.r=r;
}
pulic void run(){
while(true){
r.out();
}
main中
class ProducerConsumerDemo{
main{
Resource r = new Resource();
Producer pro= new Producer(r);
Consumer con=new Consumer(r);
Thread t1= new Thread(pro);
Thread t2= new Thread(con);
t1.start();
t2.start();
}
}
}
上面如果是一条生产一条消费就不会有问题
但是当改为多生产多消费时,就会产生线程安全问题。
可将if(flag)改为while(flag),当改为这种方式时,又会产生死锁,
解决方案:将notify改为notifyAll
。但是这样会把应该等待的线程唤醒,没有意义。针对这个问题,jdk升级之为1.5之后有解决的方案
jdk以后将同步和锁封装成了对象lock
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作
代码示例:
Lock lock=new ReentrantLock();
void show(){
lock.lock();//获取锁
代码...
lock.unlock();//释放锁
}
通过已有的锁获取该锁上的监视器对象
Condition con=lock.newCondition();
con.await();//线程等待
con.singalAll();//唤醒全部
使用lock产生的线程安全解决:
一个锁创建两个监视器对象,用来唤醒对方
Condition Protur_con=lock.newCondition();
Condition Consumer_con=lock.newCondition();
将singalAll改为 Protur_con.signal();或Consumer.signal();
【wait和sleep的区别】
wait:释放执行权,释放锁
sleep:释放执行权,不释放锁
【线层interrupt】
如果线程处于冻结状态,无法读取标记,如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。
但是强制动作会发生了InterruptedException记得处理
【守护线程】
setDaemon();
将线程标记为守护线程(也就是后台线程),必须在线程启动前调用。
【join方法】
让一个线程申请加入,运行,临时加入一个线程运算时可以使用join方法
如果前台线程结束了,那么被标记为守护线程的线程也会被结束