1.1 线程间通信
其实就是多个线程在操作同一个资源,但是操作的动作不同。
比如一个线程给一个变量赋值,而另一个线程打印这个变量。
1.2 等待唤醒机制
wait():将线程等待,释放了CPU执行权,同时将线程对象存储到线程池中。
notify():唤醒线程池中一个等待的线程,若线程池有多个等待的线程,则任意唤醒一个。
notifyAll():唤醒线程池中,所有的等待中的线程。
这三个方法都要使用在同步中,因为要对持有锁的线程进行操作。
比如,A锁上的线程被wait了,那么这个线程就进入了A锁的线程池中,只能被A锁的notify唤醒,而不能被不同锁的其他线程唤醒。
所以这三个方法要使用在同步中,因为只有同步才具有锁。
而锁可以是任意对象,这三个方法被锁调用,所以这三个方法可以被任意对象调用,所以这三个方法定义在Object类中。
wait()和sleep()的区别:
wait():可以指定等待的时间,也可以不指定时间,如果不指定时间,就只能被同一个锁的notify或notifyAll唤醒。wait时线程会释放CPU执行权,并且会释放锁。
sleep():必须指定线程休眠的时间,线程休眠即暂停执行。时间到了,线程就自动苏醒,恢复运行。sleep时线程会释放执行权,但不释放锁。
线程的停止:
1,如果run()方法中定义了循环,可以用循环结束标记,跳出循环,则线程就停止了。
2,如果线程已被冻结,读不到循环结束标记,则需要通过Thread类的interrupt方法中断线程,让线程重新获得执行的资格,从而可以读到循环结束标记,而结束线程。
3,setDaemon(true)方法将当前线程标记为守护线程,当运行的线程都是守护线程时,则Java虚拟机退出。该方法必须在启动线程前调用。
等待唤醒机制代码,实现两个线程交替执行,在控制台上交替打印两个字符串。
等待和唤醒是同一个锁r:
[java] view plaincopy
- //两个线程交替执行,在控制台交替打印两串字符串。
- class Res{
- String name;
- String sex;
- boolean flag = false; //等待唤醒机制
- }
- class Input implements Runnable{
- private Res r;
- Input(Res r){
- this.r = r;
- }
- public void run(){
- int x = 0;
- while(true){
- synchronized(r){ //等待和唤醒,是同一个锁。
- if(r.flag) //等待唤醒机制,true则等待,false则执行
- try{r.wait();}catch(Exception e){} //线程等待,进入线程池
- if(x == 0){
- r.name = "LuoQi";
- r.sex = "man";
- }
- else{
- r.name = "丽丽"; //赋值时,赋值了name还没赋值sex,就打印“lili----male”,加同步锁,牢记同步前提。
- r.sex = "女";
- }
- x = (x+1)%2;
- r.flag = true; //等待唤醒机制
- r.notify(); //任意唤醒线程池里的一个被等待的线程 //等待唤醒机制
- }
- }
- }
- }
- class Output implements Runnable{
- private Res r;
- Output(Res r){
- this.r = r;
- }
- public void run(){
- while(true){
- synchronized(r){ //等待和唤醒,是同一个锁。
- if(!r.flag) //等待唤醒机制,false则等待,true则执行
- try{r.wait();}catch(Exception e){} //线程等待,进入线程池
- System.out.println(r.name+"----"+r.sex);
- r.flag = false; //等待唤醒机制
- r.notify(); //唤醒Input线程 //等待唤醒机制
- }
- }
- }
- }
- class ThreadCommunication{
- public static void main(String[] args){
- Res r = new Res();
- Input in = new Input(r);
- Output out = new Output(r);
- Thread t1 = new Thread(in);
- Thread t2 = new Thread(out);
- t1.start();
- t2.start();
- }
- }
以上代码中,把两个同步代码块中的代码,封装成两个同步方法,一个更改两个字段的值,另一个打印两个字段的值。
两个同步方法写在Res类中,这样同步锁都是Res.class字节码文件,保证了等待和唤醒是同一个锁:
[java] view plaincopy
- //两个线程交替执行,在控制台交替打印两串字符串。
- class Res{
- String name;
- String sex;
- boolean flag = false;
- public synchronized void setRes(String name,String sex){ //同步函数
- if(this.flag) //flag为true,则线程等待进入线程池。
- try{this.wait();}catch(Exception e){}
- this.name = name; //flag为false,则线程继续执行。
- this .sex = sex;
- this.flag = true;
- this.notify(); //任意唤醒线程池中一个等待的线程
- }
- public synchronized void getRes(){
- if(!this.flag) //flag为false,则线程等待进入线程池。
- try{this.wait();}catch(Exception e){}
- System.out.println(this.name+"----"+this.sex); //flag为true则继续执行
- this.flag = false;
- this.notify(); //任意唤醒线程池中一个等待的线程
- }
- }
- class Input implements Runnable{
- private Res r;
- Input(Res r){
- this.r = r;
- }
- public void run(){
- int x = 0;
- while(true){
- if(x == 0)
- r.setRes("LuoQi","man");
- else
- r.setRes("丽丽","女");
- x = (x+1)%2;
- }
- }
- }
- class Output implements Runnable{
- private Res r;
- Output(Res r){
- this.r = r;
- }
- public void run(){
- while(true){
- r.getRes();
- }
- }
- }
- class ThreadCommunication2{
- public static void main(String[] args){
- Res r = new Res();
- Input in = new Input(r);
- Output out = new Output(r);
- Thread t1 = new Thread(in);
- Thread t2 = new Thread(out);
- t1.start();
- t2.start();
- }
- }