Java学习笔记(线程间通信)

时间:2021-11-09 20:31:32

线程间通信

 

线程间通信

思考1wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

1.      这些方法存在与同步中。

2.      使用这些方法时必须要标识所属的同步的锁。

3.      锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

思考2wait(),sleep()有什么区别?

         wait():释放资源,释放锁。

         Sleep():释放资源,不释放锁。

wait()sleep()方法的异同点:

两个方法都可以让线程处于冻结状态。

sleep必须指定时间,wait可以指定时间,也可以不指定。

sleep()方法会释放执行权,不会释放锁。

wait()方法会释放执行权,会释放锁。

 

线程间通讯:

其实就是多个线程在操作同一个资源,但是操作的动作不同。

 

多线程(线程间通信-等待唤醒机制)

Wait()

notify();

notifyAll();

都使用在同步中,因为要对持有监视器()的线程操作。

所以要使用在同步中,因为只有同步才具有锁。

为什么这些操作线程的方法要定义Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,

只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。

不可以对不同锁中的线程进行唤醒。

也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

synchronized(obj)

{

   obj.wait();

}

public synchronized void fun()

{

   this.wait();

}

public class MyClass

{

public staticsynchronized void fun1()

 {

       MyClass.class.wait();

}

}

 

线程间通信—生产者消费者:

         无论是同步函数,还是同步代码块,所做的都是隐式操作。

       同步函数或同步代码块,使用的锁和监视器是同一个。

     Lock接口:是将锁进行单独对象的封装。而且提供了对锁对象的很多功能。

     比如lock()获取锁, unlock()释放锁。

     Lock对锁的操作都是显示操作。

     所以它的出现要比同步函数或者同步代码块明确的多。而且更符合面向对象思想。

        简单一句话:Lock接口的出现替代了同步。

       好处:将同步当中的隐式锁对象封装成了显示锁操作。

            以前的锁是任意对象,现在的是指定对象Lock,Lock时必须写finally

//生产者

class Producer implements Runnable

{

         privateResource r;

         Producer(Resourcer)

         {

                   this.r= r;

         }

         publicvoid run()

         {

                   while(true)

                   {

                   r.set("馒头");

                   }

         }

}

//消费者

class Consumer implements Runnable

{

         privateResource r;

         Consumer(Resourcer)

         {

                   this.r= r;

         }

         publicvoid run()

         {

                   while(true)

                   {

                   r.get();

                   }

         }

}

原来在同步中,锁和监视器是同一个对象。

现在升级后,锁是一个单独的对象。

而且将监视器的方法也单独封装到了一个对象中,这个对象就是升级后的condition,升级后,都进行了单独的封装。

锁被封装成了Lock对象。

监视器方法都被封装到了Condition对象(监视器对象)中。

说白了,Lock替代了同步中的同步,Conditon替代了Object中的监视器方法。

Condition中提供了监视器的方法:await(),signal(),signalAll()

如何让锁和监视器产生联系?

直接通知Lock接口中的newCondition()方法就可以获取到能绑定到该Lock对象上的监视器对象Condition

代码:

class ProConDemo3

{

         publicstatic void main(String[] args)

         {

         Resourcer = new Resource();

         Producerpro = new Producer(r);

         Consumercon = new Consumer(r);

         //两个线程负责生产。

         Threadt0 = new Thread(pro);

         Threadt1 = new Thread(pro);

         //两个线程负责消费。

         Threadt2 = new Thread(con);

         Threadt3 = new Thread(con);

         t0.start();

         t1.start();

         t2.start();

         t3.start();

         }

}

 

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。

为什么定义notifyAll

因为需要唤醒对方线程。

因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。

JDK1.5中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。

Object中的waitnotify  notifyAll,替换了condition对象。

该对象可以Lock锁进行获取。

该示例中,实现了本方只唤醒对方操作。

 

线程间通讯总结:

         使用wait()notify()notifyAll()方法可以完成线程间的通讯,可叫它们通讯方法;

         只能在同步环境下调用通讯方法;

         只能使用监视器对象调用通讯方法;

         每个监视器对象都有一个线程*:执行a.wait()的线程会被关押到a对象的线程*中;

         若想释放出a对象的线程*中的线程,那么需要调用a.notify()文法,该方法只能保证在a对象的线程*中释放出一个线程,但不能保证释放的是哪一个;

         还可以使用a.notifyAll()方法释放出a对象的*中关押的所有线程。

       wait()了的线程不能自己恢复到就绪状态,只能等待其他线程调用同一监视器对象上的notify()notifyAll()方法来唤醒。

         wait()了的线程会释放监视器对象的对象锁,这样其他线程就可以进入他占用的同步环境。

         被唤醒的线程恢复到了就绪状态,当再次获取监听器对象的锁后会在wait()处向下运行。

 

停止线程

1.定义循环结束标记

         因为线程运行代码一般都是循环,只要控制了循环即可。

2.使用interrupt(中断)方法。

         该方法是结束线程的冻结状态,使线程回到运行状态中来。

注:stop方法已经过时不再使用。

stop方法已经过时。

如何停止线程?

只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构。

只要控制循环,就可以让run方法结束,也就是线程结束。

public void run()

{

while(true)

{

System.out.println("ok");

}

}

特殊情况:

当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

Thread类提供该方法interrupt();

如果读不到标记怎么办?

比如在任务中让线程处于了冻结状态。

释放了执行资格,无法执行标记,run方法没结束,线程也无法结束。

Thread类中有一个interupt方法。可以将线程的冻结状态清除,让线程恢复到具备执行资格。

例:

class StopThread implements Runnable

{

private booleanflag = true;

publicsynchronized void run()

{

while(flag)

{

try{

wait();

}catch (InterruptedException e){

System.out.println(Thread.currentThread().getName()+"...."+e.toString());

flag = false;

}

System.out.println(Thread.currentThread().getName()+"......run");

}

}

public voidsetFlag()

{

flag = false;

}

}

class StopThreadDemo

{

public staticvoid main(String[] args)

{

StopThread st = new StopThread();

Thread t1 = new Thread(st);

Thread t2 = new Thread(st);

t1.start();

t2.start();

int num = 1;

while(true)

{

if(++num==50)

{

// st.setFlag();//将标记置为false。让t1t2对应的线程结束。

//清除t1的冻结状态。

t1.interrupt();

t2.interrupt();

break;

}

System.out.println(Thread.currentThread().getName()+"....."+num);

}

System.out.println("over");

}

}

 

使用interrupt()方法:

中断方法:interrupt()

Thread类中有一个boolean类型的属性,我们叫中断值,这个中断值默认为false

当调用了线程对象的interrupt()方法后,这时中断值为true

所有的声明了InterruptedException异常的方法都会在中断值为true时抛出这个异常。

Thread.sleep()

Object.wait()

Thread.join()

当这些方法抛出异常之后,中断值会恢复到false

 

isInterrupted()方法和interrupted()的作用:

isInterrupted() –实例方法

  获取该线程的中断状态,该方法不会去修改中断值,只是对中断值进行读操作。

interrupted() –静态方法

  获取当前线程的中断状态,该方法会在获取中断值之后,把中断值修改为false。然后返回修改之后获取到的值。boolean b =中断值;中断值=false;return b

interrupt() –实例方法 

把当前中断值设置为true

 

setDaemon()将线程标记为守护线程或用户线程(后台线程),一定要在start之前调用。

多线程(Join方法)

A线程执行到了B线程的join()方法时,A就会等待,等B线程都执行完,A才会执行。

Join可以用来临时加入线程执行。

join() 等待该线程终止。当前线程等待调用此方法的线程执行完才执行。

凡是让线程处于中断的方法都需要抛出 InterruptedException异常。

join()方法的作用:

package cn.itcast.thread.join;

public class Test

{

  public static void main(String[] args) throws InterruptedException

   {

      Thread th = new Thread()

         {

         public void run()

           {

              for(int i = 0; i < 10; i++)

                   {

                 System.out.println(i);

                 try {

                            Thread.sleep(500);

                     }catch(InterruptedException e) {}

                   }

         }

      }

      th.start();

      th.join();       //让当前线程(主线程)等待该线程(th)结束,再向下执行。

      System.out.println("程序结束!!!");

   }

}

 

让当前线程等待该线程结束再向下运行。

当前线程:语句是谁执行的,谁就是当前线程。

该线程:你调用了哪个线程对象的方法,哪个线程就是该线程。

 

多线程(优先级与yield方法)

yield()临时暂停线程,其为静态方法。

yield()方法的作用:

Thread.yield();     //暂停当前正在执行的线程对象,并执行其他线程。

当前线程执行了这条语句,表示当前线程已经完成最重要的部分,然后跟调度说,我可以休息了,你可以去为其他线程服务了,但当前线程不会进入阻塞状态,还是就绪状态。

让步不同与阻塞,让步之后,线程还是就绪状态。