Java多线程编程核心技术 —— 线程间通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lkp1603645756/article/details/84174453

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体。线程间的通信就是成为整体的比用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对个线程任务在处理的过程中进行有效的把控与监督。

1、wait使线程停止运行,而notify使停止的线程继续运行。

2、尝试一下:

出现的原因是没有“对象监视器”,也就是没有同步加锁。解决方案:synchronized(newString),把wait方法放到synchronized块中。

3、关键字synchronized可以将任何一个Object对象作为同步对象来看待,而Java为每个Object都实现了wait()和notify()方法,它们必须用在被synchronized同步的Object的临界区内。

4、wait()方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。

      notify()方法可以随机唤醒等待队列中等待统一共享资源的“一个”线程,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知“一个”线程。

     notifyAll()方法可以使所有正在等待队列中等待同一共享资源的“全部”线程从等待状态退出,进入可运行状态。此时,优先级最高的那个线程最先执行,但也有可能是随机执行,因为这要取决于JVM虚拟机的实现。

5、线程状态示意图

扫描二维码关注公众号,回复: 4240084 查看本文章

1)新创建一个新的线程对象后,在调用它的start()方法,系统会为此线程分配CPU资源,使其处于Runnable(可运行)状态,这是一个准备运行的阶段。如果线程抢占到CPU资源,此线程就处于Running(运行)状态。

2)Runnable状态和Running状态可相互切换,因为有可能线程运行一段时间后,有其他高优先级的线程抢占了CPU资源,这时次线程就从Running状态编程Runnable状态。

线程进入Runnable状态大体分为如下5种情况:

  • 调用sleep()方法后经过的时间超过了指定的休眠时间。
  • 线程调用的阻塞IO已经返回,阻塞方法执行完毕。
  • 线程成功地获得了试图同步的监视器。
  • 线程正在等待某个通知,其他线程发出了通知。
  • 处于挂起状态的线程调用了resume回复方法。

3)Blocked是阻塞的意思,例如遇到了一个IO操作,此时CPU处于空闲状态,可能会转而把CPU时间片分配给其他线程,这时也可以称为“暂停”状态。Blocked状态机结束后,进入Runnable状态,等待系统重新分配资源。

出现阻塞的情况大体分为如下5种:

  • 线程调用sleep方法,主动放弃占用的处理器资源。
  • 线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞。
  • 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。
  • 线程等待某个通知。
  • 程序调用了suspend方法将该线程挂起。此方法容易导致死锁,尽量避免使用该方法。

4)run()方法运行结束后进入销毁阶段,这个线程执行完毕。

每个所对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获取锁的线程,阻塞队列存储 被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU的调用;反之,一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。

6、当方法wati()被执行后,锁被自动释放,但执行完notify()方法,锁却不自动释放。

7、方法notify()仅随机唤醒一个线程。当多次调用notify()方法时,会随机将等待wait状态的线程进行唤醒。

8、带一个参数的wait(long)方法的功能是等待某一事件内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。

9、在使用wait/notify模式时,还需要注意另外一种情况,也就是wait等待的条件发送了变化,也容易造成程序逻辑的混乱。

10、等待/通知模式最经典的案例就是“生产者/消费者”模式。但此模式在使用上有几种“变形”,还有一些小的注意事项,但原理都是基于wait/notify的。

11、在Java语言中提供了各种各样的输入/输出流Stream,使我们能够很方便地对数据进行操作,其中管道流(pipeStream)是一个种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输入管道中读数据。通过使用管道,实现不同线程间的通信,而无需借助于类似临时文件之类的东西。

在Java的JDK中提供了4个类来使线程间进行通信:

1)PipedInputStream和PipedOutputStream

2)PipedReader和PipedWriter

Join方法

12、在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时计算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后在结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。

13、方法join的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码。

14、方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理做为同步。

15、在join过程中,如果当前线程对象被中断,则当前线程出现异常。

16、方法join(long)的功能在内部是使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。当前线程锁被释放,那么其他线程就可以调用此线程中的同步方法了。而Thread.sleep(long)方法却不释放锁。

类ThreadLocal的使用

17、变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static变量。如果想实现每一个线程都有自己的共享变量该如何解决呢?JDK中提供的类ThreadLocal正式为了解决这样的问题。

18、类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。

19、ThreadLocal类解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值是可以放入ThreadLocal类中进行保存的。


类InheritableThreadLocal的使用

20、使用InheritableThreadLocal类可以让子线程从父线程中取得值。

21、使用InheritableThreadLocal类需要注意一点的是,如果子线程在取得值的同时,主线程将InheritableTHreadLocal中的值进行更改,那么子线程取到的值还是旧值。

猜你喜欢

转载自blog.csdn.net/lkp1603645756/article/details/84174453