线程间通信

1.等待/通知机制

什么是等待、通知呢? 就比如说有一家餐厅,厨师负责做饭,服务员负责上菜,当菜在做的时候,服务员就需要等待,想象一下,假如没有通知,服务员就要每过一会过去看看菜到底做好没有,而这时候前面也有很多工作需要他做,这样他就会多做很多无用功,那么有了通知以后,厨师做完菜以后,就摁下喇叭或者其他方式通知一下服务员需要上菜了,这样服务员就只收到通知的时候过来端菜,就可以安心忙前面的事了。

1.等待、通知机制是用什么实现的呢?

通过wait()、notify()或notifyall()方法来实现。这三个方法都是属于Object类的。

wait()是让线程等待的,好像和sleep()有点像,他们之间的区别可以看这篇http://www.cnblogs.com/wxw7blog/p/7390387.html。

notify()、notifyall()是来唤醒wait的线程的,也就是通知的。notify()是唤醒单个线程,假如很多线程都wait()了,会随机唤醒一个等待的线程,而notifyall()会唤醒所有在等待的线程。

 

2.同步方法里面执行到wait()锁会马上释放,而同步方法里面执行到notify()锁不会马上释放,只有等到同步里面的代码执行完才能释放锁。

3.wait(long) wait方法里面也可以有参数,如何在这个时间内线程没有被唤醒,会自动唤醒。

4.通知过早

这个情况发生在负责唤醒的线程早于需要等待的线程之前运行了,这时候唤醒没有什么意义了,等待线程会一直处于等待状态下。

解决方法呢?

就是在里面加一个判断语句

class S{
 boolean isfirstRunb=fasle;
}
  线程A{
      S s;
      public A(S s){
        this.s=s; 
     }
     synchroized public void run(){
       while(s.isfirstRunb==fasle){
          s.wait;
     }
   }
  }
 线程B{
      S s;
      public B(S s){
        this.s=s; 
     }
     synchroized public void run(){
          s.notify();
          s.isfirstRunb=true;
     }
   }
  }

这样就可以了在通知过早的情况下再执行等待命令

5.等待wait的条件发生变化

  也就是唤醒它的时候线程回来执行wait()方法下面的代码,可是这时候可能条件已经发生变化,所以外围最好是用一个循环来包围,而不是if,让他回来再检查一遍。

6.生产者、消费者模式实现

      等待、通知机制最常用的应该就是生产者与消费者模式了。

 7,通过管道进行线程间通信

     管道流用于在线程间进行通信

分为四种

 

 

 

   

2.join的使用

join()的作用是让当前线程等待调用join()方法的线程执行完毕以后才能执行。

{
   Thread t=new Thread();
   t.start();
   t.join();
  

}

 

 这样主线程就会等待t线程执行完以后再执行。

join(long)也可以使用,也是等待long时间之后就会运行,那和sleep(long)有啥区别呢?

 

区别就是join()内部是通过wait()来实现的,会释放锁,而sleep()不会释放锁

 这里一定要注意的是,join()方法也会抢锁!

 3.ThreadLocal的使用

ThreadLocal是线程是为了提供每个线程所私有的共享变量。

ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

举个例子,我出门需要先坐公交再做地铁,这里的坐公交和坐地铁就好比是同一个线程内的两个函数,我就是一个线程,我要完成这两个函数都需要同一个东西:公交卡(北京公交和地铁都使用公交卡),那么我为了不向这两个函数都传递公交卡这个变量(相当于不是一直带着公交卡上路),我可以这么做:将公交卡事先交给一个机构,当我需要刷卡的时候再向这个机构要公交卡(当然每次拿的都是同一张公交卡)。这样就能达到只要是我(同一个线程)需要公交卡,何时何地都能向这个机构要的目的。

有人要说了:你可以将公交卡设置为全局变量啊,这样不是也能何时何地都能取公交卡吗?但是如果有很多个人(很多个线程)呢?大家可不能都使用同一张公交卡吧(我们假设公交卡是实名认证的),这样不就乱套了嘛。现在明白了吧?这就是ThreadLocal设计的初衷:提供线程内部的局部变量,在本线程内随时随地可取,隔离其他线程。

作者:winwill2012
链接:https://www.zhihu.com/question/23089780/answer/62097840
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

ThreadLocal基本操作

构造函数

ThreadLocal的构造函数签名是这样的:

  /**
     * Creates a thread local variable.
     * @see #withInitial(java.util.function.Supplier)
     */
    public ThreadLocal() {
    }

内部啥也没做。

initialValue函数

initialValue函数用来设置ThreadLocal的初始值,函数签名如下:

    protected T initialValue() {
        return null;
    }

该函数在调用get函数的时候会第一次调用,但是如果一开始就调用了set函数,则该函数不会被调用。通常该函数只会被调用一次,除非手动调用了remove函数之后又调用get函数,这种情况下,get函数中还是会调用initialValue函数。该函数是protected类型的,很显然是建议在子类重载该函数的,所以通常该函数都会以匿名内部类的形式被重载,以指定初始值,比如:

package com.winwill.test;

/**
 * @author qifuguang
 * @date 15/9/2 00:05
 */
public class TestThreadLocal {
    private static final ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return Integer.valueOf(1);
        }
    };
}

get函数

该函数用来获取与当前线程关联的ThreadLocal的值,函数签名如下:

public T get()

如果当前线程没有该ThreadLocal的值,则调用initialValue函数获取初始值返回。

set函数

set函数用来设置当前线程的该ThreadLocal的值,函数签名如下:

public void set(T value)

设置当前线程的ThreadLocal的值为value。

remove函数

remove函数用来将当前线程的ThreadLocal绑定的值删除,函数签名如下:

public void remove()

在某些情况下需要手动调用该函数,防止内存泄露。

 

每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object。

 

posted @ 2017-08-18 16:14  竹马今安在  阅读(197)  评论(0编辑  收藏  举报