线程间的等待唤醒机制
什么是线程的等待唤醒机制?
在前面说到线程的状态的时候,大家注意到冻结状态分为等待态和睡眠态,睡眠态是线程休息一会接着自己玩,而等待态是一旦等待了,就只能干瞪眼了,除非有人来给他叫醒了,才能接着和大家一起玩。
等待唤醒机制什么时候使用呢?
当一个资源的文件同时被多个线程存储和读取时,这就得有等待唤醒了。比较经典的是生产者和消费者的例子。总之就是你消费得跟上我生产的进度,我要产不出来,你就在那等着,等我有存货了你在拿着消费去。
等待唤醒机制都有什么?
wait();方法:让线程进入等待态,放弃执行权,但有执行资格。
notify();方法 唤醒之前进入等待态的线程,谁先等的就先叫谁,先进先出。
notifyAll();方法 唤醒线程池所有的等待线程。
通过查阅API发现,这些方法都被定义在了超类Object类中这事为什么呢?
因为为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以 被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
代码示例:
其实上述的示例,我们多启动几个线程就会发现,程序停那不动了,这是因为所有线程都不满足条件进入了等待态,就相当于程序挂了,那该怎么解决呢?答案当然是notifyAll了,下面我们可能看这个经典的消费者和生产者的例子。
消费者和生产者代码示例:
用notifyAll每次唤醒所有等待线程是不是跟麻烦呢,可不可以只唤醒我想让他醒的线程呢?
这个在JDK1.5之前是个dream,但是JDK1.5了就是小意思了,在JDK 1.5中Java给出来新的解决方案:
1. 将同步synchrozed 替换成 显示 Lock 操作
2. 将Object 中的 wait ,notify,notifyAll,替换成了condition 对象。
3. condition 对象可以Lock锁,进行获取。
这时候我们就能通过condition对象来控制唤醒线程了,升级后的代码示例:
这时候大家是不是会想synchrozed被替代了,那是不是就不能用了呢?
这当然不是了,synchrozed关键字在Java中还是保留的,主要还是使用场景了,哪个合适就是用哪个了。就像单例设 计中懒汉式,一个synchrozed足以搞定。
线程的中断操作
什么是中断?
就是让线程停止操作,而且是强制性的,停了你没商量。
在之前的版本中,我们可以用stop()方法让线程中断操作。可现在stop由于种种原因吧被取消了,那怎么中端线程的操作呢?
其实中断线程我们通过循环就可以操作,一般线程内都是执行的循环操作,让循环终止,线程也就没事干了,然后就停止了。让循环中断我们一般会用标记来操作,可以但遇到同步就不好用了,所以Java在Thread类又提供了一个interrupt方法, 让冻结状态的线程强制回到正常态。
interrupt方法代码示例:
线程的守护线程和用户线程
什么是守护线程?
说白了就是后台线程,不再前台运行了,一旦用户进程停止了,守护线程也就over了,守护线程守护的就是用户线程。
怎么标记守护线程?
线程对象.setDaemon(true);将该线程标记为守护线程或用户线程。
需要注意的是,标记时必须在线程启动前标记!当正在运行的线程都是守护线程时,Java 虚拟机退出
Thread类的jion()方法
join()方法有什么功能呢?
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
简单点说,join就是用来插队的,一旦有线程调用了join方法就会一直执行,让别的进程干瞪眼,等它执行结束了,别的线程才会继续执行。
线程的优先级
在线程执行的时候都有一个默认的优先级,我们可以使用Thread类提供的toString方法查看一下。
在Java中线程的优先级,一共分为十级,十级最高,一级最低。
其中MAX_PRIORITY(最大优先级), MIN_PRIORITY(默认优先级), NORM_PRIORITY(最小优先级)
使用格式为:
线程对象.setPriority(优先级别);
要注意的是优先级只是提升的抢夺CPU资源的几率,并不是霸占。
无私的yield()方法
为什么要说yield()方法无私呢,因为有线程一用它吧,自己就歇菜了,让别人玩去了。
比较官方的说法就是暂停当前执行的线程,并执行其他线程。
定义格式:
Thread.yield();
什么是线程的等待唤醒机制?
在前面说到线程的状态的时候,大家注意到冻结状态分为等待态和睡眠态,睡眠态是线程休息一会接着自己玩,而等待态是一旦等待了,就只能干瞪眼了,除非有人来给他叫醒了,才能接着和大家一起玩。
等待唤醒机制什么时候使用呢?
当一个资源的文件同时被多个线程存储和读取时,这就得有等待唤醒了。比较经典的是生产者和消费者的例子。总之就是你消费得跟上我生产的进度,我要产不出来,你就在那等着,等我有存货了你在拿着消费去。
等待唤醒机制都有什么?
wait();方法:让线程进入等待态,放弃执行权,但有执行资格。
notify();方法 唤醒之前进入等待态的线程,谁先等的就先叫谁,先进先出。
notifyAll();方法 唤醒线程池所有的等待线程。
通过查阅API发现,这些方法都被定义在了超类Object类中这事为什么呢?
因为为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以 被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
代码示例:
/*
需求:
有一个资源一边存数据一边取数据 同时进行
*/
class IntputOutputDemo2//优化后代码
{
public static void main(String[] args)
{
Res r = new Res();
new Thread(new Intput(r)).start();
new Thread(new Output(r)).start();
}
}
class Res
{
private String name;
private String sex;
//定义标记 判读是否已存数据
private boolean flag=false;
public synchronized String set(String name;String sex)
{
if(!flag)
try{this.wait();}catch(Exception e){}
this.name=name;
this.sex=sex;
this.flag=true;
this.notify();
}
public synchronized void get()
{
if(flag)
try{this.wait();}catch(Exception e){}
System.out.printf(this.name+"----"this.sex);
this.flag=false;
this.notify();
}
}
class Intput implements Runnable
{
//建立一个引用
private Res r;
Intput(Res r)
{
this.r=r;
}
public void run()
{
int x = 0;
//存数据
while(true)
{
if(x==0)
{
r.set(r.name="mike",r.sex="man- man - man");
}
else
{
r.set(r.name="丽丽",r.sex="女- 女 - 女");
}
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r= r;
}
public void run()
{
//取数据
while(true)
{
r.get();
}
}
}
通过示例,大家发现我们只用到了wait()和notify()方法;那notifyAll()什么时候用呢?其实上述的示例,我们多启动几个线程就会发现,程序停那不动了,这是因为所有线程都不满足条件进入了等待态,就相当于程序挂了,那该怎么解决呢?答案当然是notifyAll了,下面我们可能看这个经典的消费者和生产者的例子。
消费者和生产者代码示例:
/*
生产者和消费着的例子
多个线程同时生产,和多个产品同时消费
*/
class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
//定义资源
class Resource
{
private String name ;
private int count=1;//定义计数器
//定义标记
private boolean flag = false;
public synchronized void set(String name)
{
while(flag)//之所用while 是要循环判断标记
try
{
this.wait();//当发现已经生产时 就近入睡眠状态
}
catch (Exception e)
{
}
this.name=name+"--"+count++;//连编号一起赋值
System.out.println(Thread.currentThread().getName()+"--生产者--"+this.name);
flag = true;
this.notifyAll();//唤醒所有睡眠的线程
}
public synchronized void out()
{
while(!flag)
try
{
this.wait();
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"--消费者----------"+this.name);
flag = false;
this.notifyAll();
}
}
//定义生产者
class Producer implements Runnable
{
private Resource r ;
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.set("+商品+");
}
}
}
class Consumer implements Runnable
{
private Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run ()
{
while(true)
{
r.out();
}
}
}
JDK 1.5 新特性用notifyAll每次唤醒所有等待线程是不是跟麻烦呢,可不可以只唤醒我想让他醒的线程呢?
这个在JDK1.5之前是个dream,但是JDK1.5了就是小意思了,在JDK 1.5中Java给出来新的解决方案:
1. 将同步synchrozed 替换成 显示 Lock 操作
2. 将Object 中的 wait ,notify,notifyAll,替换成了condition 对象。
3. condition 对象可以Lock锁,进行获取。
这时候我们就能通过condition对象来控制唤醒线程了,升级后的代码示例:
/*
生产者和消费着的例子
多个线程同时生产,和多个产品同时消费
jdk1.5新特性
*/
import java.util.concurrent.locks.*;
class ProducerConsumerDemo2
{
public static void main(String[] args)
{
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
//定义资源
class Resource
{
private String name ;
private int count=1;//定义计数器
//定义标记
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition Condition_p = lock.newCondition();
private Condition Condition_c= lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();
try
{
while(flag)//之所用while 是要循环判断标记
Condition_p.await();
this.name=name+"--"+count++;//连编号一起赋值
System.out.println(Thread.currentThread().getName()+"--生产者--"+this.name);
flag = true;
Condition_c.signal();
}
finally
{
lock.unlock();
}
}
public synchronized void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
Condition_c.await();
System.out.println(Thread.currentThread().getName()+"--消费者----------"+this.name);
flag = false;
Condition_p.signal();
}
finally
{
lock.unlock();
}
}
}
//定义生产者
class Producer implements Runnable
{
private Resource r ;
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
try
{
r.set("+商品+");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run ()
{
while(true)
{
try
{
r.out();
}
catch (InterruptedException e)
{
}
}
}
}
这时候大家是不是会想synchrozed被替代了,那是不是就不能用了呢?
这当然不是了,synchrozed关键字在Java中还是保留的,主要还是使用场景了,哪个合适就是用哪个了。就像单例设 计中懒汉式,一个synchrozed足以搞定。
线程的中断操作
什么是中断?
就是让线程停止操作,而且是强制性的,停了你没商量。
在之前的版本中,我们可以用stop()方法让线程中断操作。可现在stop由于种种原因吧被取消了,那怎么中端线程的操作呢?
其实中断线程我们通过循环就可以操作,一般线程内都是执行的循环操作,让循环终止,线程也就没事干了,然后就停止了。让循环中断我们一般会用标记来操作,可以但遇到同步就不好用了,所以Java在Thread类又提供了一个interrupt方法, 让冻结状态的线程强制回到正常态。
interrupt方法代码示例:
class StopThread implements Runnable
{
private boolean flag =true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
//抛出异常 并置换标记 结束进程
System.out.println(Thread.currentThread().getName()+"Exception");
flag = false;
}
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag()
{
flag = false;
}
}
class InterruptDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while(true)
{
if(num++ == 60)
{
//st.changeFlag();
t1.interrupt();//强制中断等待态的线程 让其继续执行
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
线程的守护线程和用户线程
什么是守护线程?
说白了就是后台线程,不再前台运行了,一旦用户进程停止了,守护线程也就over了,守护线程守护的就是用户线程。
怎么标记守护线程?
线程对象.setDaemon(true);将该线程标记为守护线程或用户线程。
需要注意的是,标记时必须在线程启动前标记!当正在运行的线程都是守护线程时,Java 虚拟机退出
Thread类的jion()方法
join()方法有什么功能呢?
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
简单点说,join就是用来插队的,一旦有线程调用了join方法就会一直执行,让别的进程干瞪眼,等它执行结束了,别的线程才会继续执行。
线程的优先级
在线程执行的时候都有一个默认的优先级,我们可以使用Thread类提供的toString方法查看一下。
在Java中线程的优先级,一共分为十级,十级最高,一级最低。
其中MAX_PRIORITY(最大优先级), MIN_PRIORITY(默认优先级), NORM_PRIORITY(最小优先级)
使用格式为:
线程对象.setPriority(优先级别);
要注意的是优先级只是提升的抢夺CPU资源的几率,并不是霸占。
无私的yield()方法
为什么要说yield()方法无私呢,因为有线程一用它吧,自己就歇菜了,让别人玩去了。
比较官方的说法就是暂停当前执行的线程,并执行其他线程。
定义格式:
Thread.yield();