线程间通信其实就是多个线程在同时操作同一个资源但操作的动作不同
撒旦撒旦
wait()、notify()、notifyAll()用于操作线程,为什么定义在Object中:
答:这些方法存在于同步中,使用这些方法是必须要标识所属的同步的锁,而这锁可以是任意对象,所以可以被人任意对象调用的方法应定义在Object类中
wait()和sleep()的区别:
wait()释放资源,释放锁,而sleep只释放资源不释放锁(监听器)
线程通信----生产者与消费者程序代码:
class Resource{
private int count=0;//定义产品数目
private boolean flag=false;//标记生产还是消费
public synchronized void set(){//生产流程,对其加上锁
while(flag)//用while循环是为了实现当多个生产的时候应该多次判断,例如生产的时候,
//执行了notifyAll而此时线程中的另一个生产在wait状态下如果用的是if的或将不能判断flag了会出现安全问题,因此一定要用while进行多次判断
try{this.wait();}catch(Exception e){}
count++;
System.out.println(Thread.currentThread().getName()+"生产者"+count);//打印出生产者的信息
flag=true;//更改操作方式的标记符
this.notifyAll();//唤醒所有等待状态下的线程
}
public synchronized void out(){//消费流程
while(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"消费者"+count);
flag=false;
this.notifyAll();
}
}
/*
创建消费者并且将其继承Resource类并且实现接口Runnable
定义一个私有的Resource成员变量,覆盖接口中的run方法
*/
class Consumer extends Resource implements Runnable{
private Resource r;
Consumer(Resource r){//获得Resource对象
this.r=r;
}
public void run(){
while(true){
//try{Thread.sleep(1000);}catch(Exception e){}//添加延迟函数使结果更明显
r.out();
}
}
}
/*
创建生产者并且将其继承Resource类并且实现接口Runnable
定义一个私有的Resource成员变量,覆盖接口中的run方法
*/
class Producer extends Resource implements Runnable{
private Resource r;
Producer(Resource r){
this.r=r;
}
public void run(){
while(true){
//try{Thread.sleep(1000);}catch(Exception e){}
r.set();
}
}
}
同过这个程序也能看出线程的状态有:
1、被创建
2、运行
3、冻结(睡眠、等待)
4、堵塞状态
5、消亡
在JDK1.5之后又多了一种加锁机制
用Lock中的ReentrantLock类实现synchronized的其中味Lock.lock()和Lock.unlock()
用Condition接口实现wait、notify、notifyAll、其中wei
Condition c=lock.newCondition();
c.await()、c.signal()、c.signalAll()
可以将上面Resource类换成下面的
class Resource{
private Lock lock=new ReentrantLock();
private int count=0;
private boolean flag=false;
private Condition setLock=lock.newCondition();//对生产状态进行标记
private Condition outLock=lock.newCondition();//对消费状态进行标记
public void set()throws InterruptedException{//中断会产生异常
lock.lock();//加锁
try{
while(flag)
setLock.await();//让生产者等待
count++;
System.out.println(Thread.currentThread().getName()+".....生产者......"+count);
flag=true;
outLock.signal();//唤醒消费者中等待时间最长的线程
}
finally{//因为此用法必须得释放所所以将其放在finally中
lock.unlock();//释放所
}
}
public void out()throws InterruptedException{
lock.lock();
try{
while(!flag)
outLock.await();
System.out.println(Thread.currentThread().getName()+"消费者"+count);
flag=false;
setLock.signal();
}
finally
{
lock.unlock();
}
}
}
还有对Consumer类也要进行修改
将其run方法改为
public void run(){
while(true){
//try{Thread.sleep(1000);}catch(Exception e){}//添加延迟函数使结果更明显
try{
r.out();
}catch(InterruptedException e){}
/*out方法在上面抛出异常了所以要将其捕捉或抛出又因为run是覆盖Runnable接口中的函数,而他中没有抛出异常,所以只能将异常捕捉*/
}
}
对于Producer也应该捕捉异常
如何停止线程:
1、定义循环结束标志
因为线程运行代码一般是循环的,所以只要控制了循环就能控制线程停止
2、使用interrupt(中断)方法(中断状态就是冻结状态)
该方法是结束线程的冻结状态,是线程回到运行状态中来
综上所述:停止线程就是结束run方法
特殊情况:
当线程处于冻结状态,就不会读取到标记,那么就不会结束
当没有指定的方式让冻结的线程恢复到运行状态时,这是需要对冻结进行清除,强制让线程恢复到运行状态来,这样就可以操作标记线程结束
Thread类中提供了interrupt()方法来实现这一功能例如:
class Sleeper implements Runnable{
private String name;//定义线程的名字
Sleeper(String name){
this.name=name;
}
public void run(){
try{
System.out.println(name+"befor sleep");
Thread.sleep(3000);//让线程睡眠来查看interrupt的作用
System.out.println(name+"is asleep");
}catch(Exception e){
System.out.println(name+"is interrupt:");//通过抛出异常来验证interrupt确实可以将冻结状态拉回到运行状态
}
}
}
多线程其他常见用法:
setPriority设置线程优先级对于此方法其中传入的参数一般为MIN_PRIORITY、MAX_PRIORITY
yield()释放执行权是线程交替执行,就是让当前线程暂停
setDaemon(boolean )将当前线程设置为守护线程(用户线程/后台线程)当boolean为true的时候
总结:
wait、sleep、notify、notifyAll、interrupt、join、yield用法及区别:
wait是释放资源,释放锁,并且是Object中的方法,可以被任意对象使用,但必须带同步代码块中,它的作用是上当前线程处于等待状态;
sleep是释放资源,但是不释放所,他是Thread类中的静态方法,他可以在任意位置,但必须处理异常,它的作用是让当前线程处于睡眠状态;
notify和notifyAll也是Object中的方法,并且也要在同步代码块中,都是和wait成对出现的,它的作用是唤醒同一个锁上的等待状态的线程;
interrupt的作用是将线程中处于jion、wait、sleep状态下的线程拉回运行状态使其运行,他也会抛出异常,此方法是非静态的
join的作用是让此线程执行完之后才能让他之后的线程执行,而在此语句之前的线程不受影响,他也必须进行异常捕捉。
例如:
class JionTest
{
public static void main(String[] args)
{
//创建三个线程
Thread t1=new Thread(new TestJion("线程1.。。。。。。。。。。",500));
Thread t2=new Thread(new TestJion("线程2.。。。。。。",1000));//设置不同的睡眠时间让结果更能显示问题的所在
Thread t3=new Thread(new TestJion("线程333333333333333333333333333333333333",500));
//启动第一个和第二线程
t1.start();
t2.start();
try{
t1.join();//是第三个等待t1运行完
}catch(Exception e){}
t3.start();
}
}
class TestJion implements Runnable{//实现Runnable接口并且覆盖其run方法
String str;
int time;
TestJion(String str,int time){// 初始化信息
this.str=str;
this.time=time;
}
public void run(){
for(int i=0;i<=10;i++)
{
try{Thread.sleep(time);}catch(Exception e){}//延迟等待
System.out.println(str);
}
}
}
对于本程序等线程1执行完之后才可以运行线程3,但线程2是否执行完对线程3没有影响
yield作用是将当前线程重新拉回到可执行状态(临时状态)等待执行,但也有可能直接就执行了。yield只能是同优先级的线程有机会执行,只是有机会。