前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程编程核心技术(三)

多线程编程核心技术(三)

作者头像
用户6203048
发布2020-10-27 14:33:57
3600
发布2020-10-27 14:33:57
举报
文章被收录于专栏:JathonKatuJathonKatu

Start:第三章

Page:133-199

Date:20190202

Title:多线程编程核心技术

线程间的通信

要点:

使用wait/notify实现线程间的通信

生产者/消费者模式的实现

方法join的使用

ThreadLocal类的使用

wait/notify机制

wait会在wait时立马暂停线程的运行,而notify则会运行完该同步方法后才释放锁。(看底层C++代码的时候发现,在wait的时候调用了exit方法释放同步锁,而notify则没有,则默认在方法运行完后释放。)

wait和notify的时候,要在需要调用这个方法的线程内,lock.wait/lock.notify,初步理解为,将当前线程放入lock的wait队列里,或者唤醒lock的wait队列中的一个(每个object自带一个objectMonitor,这个是C++的源码,在java只能看到关键字native修饰的方法名)。(随机唤醒)而notifyAll则是遍历唤醒所有等待线程。

发出notify时如果没有任何等待线程,则略过。如果notify的时候没有阻塞的线程,然后再阻塞,那么相当于只执行了阻塞。

多次notify可以唤醒多个不同的线程。

notifyAll后执行的可能是优先级高的,也可能是随机,取决于jvm的实现。(这里尽量少用优先级控制执行顺序,因为优先级存在一个概率性,而不是绝对性。)

每个对象锁又两个队列,一个是就绪队列(准备执行),一个是阻塞队列(进入阻塞)

wait的线程如果调用interrupt方法会报错:InterruptedException.(人家已经阻塞了你还让人家阻塞。)

Wait(long)等待到long时间,如果没有线程唤醒这个线程,到了Long毫秒后自动唤醒。(wait() == wait(0),无限等待,直到唤醒)

还有一个Wait(Long,int),如果int大于500000(纳秒)则Long+1,否则就Long毫秒

线程的状态:

new Thread >> Start >> Runnable >> 抢到资源 >> Running

生产者/消费者模式:

生产者线程和消费者线程分别用while(true)执行run

run中生产者进行生产,只生产一个产品,然后阻塞,直到消费者消费后,消费者唤醒生产者,告诉生产者:已经没有产品可以消费了,快起来生产。然后消费者阻塞。循环执行。

一对多和多对多的生产者和消费者关系:

一对多的时候,无论多的是生产者或者消费者,都有可能唤醒同类型的线程(及生产者唤醒生产者或者消费者唤醒消费者),陷入假死状态。

多对多同理。

可以用notifyAll来解决这个问题。唤醒的同类执行之后会wait,而如果是不同类则会进行,则只会产生一个产品/消费一个产品,然后唤醒异类(由于run中的方法是同步的,不存在脏读现象)。

线程间的字节流和字符流

PipedInputStream/PipedOutputStream

PipedRead/PipedWrite

和常用的IO流用法相似,这里不深度挖掘。

写入和备份的交叉运行:

这里用到的是volatile关键字,boolean类型,用来体现是写入还是存储。

而写入和存储的方法用Synchronized修饰成为同步方法。

则保证写入和备份交叉运行,且不会同时出现多个写入或者备份。

Join方法的使用

main(){ a.join()}

事实上是对a方法进行isAlive判断。如果a方法是活跃的(非阻塞),则阻塞调用线程--也就是main线程。直到a方法执行完。

join方法底层用的是wait方法实现的,和sleep看起来相同,但事实上sleep并不会释放锁,而join/wait会释放锁。

和wait相同,join(Long),join(Long,int)分别代表等待Long毫秒,或者(如果int大于500000(纳秒)则Long+1,否则就Long)毫秒

同理join() == join(0)

当三个方法同时争夺一个锁:

A,B,Main同时争夺一个锁B,且A先执行,执行时间大于main的b.join(等待)时间,会出现三种情况:A执行完,main发现自己的等待时间结束:

1.main与B争夺锁,且main胜出,先执行main

2.main与b争夺锁,且B胜出,先执行B

3.B,main同时争夺锁,不论谁胜出,最后执行输出的时候异步输出导致输出结果异常

ThreadLocal类的使用

多个线程同时调用ThreadLocal的get可以获取各自的值,互不干扰。

这里是因为get或者set的时候会把调用这个方法的线程带入。get自己ThreadLocalMap里的Entry或者set自己的ThreadLocalMap,事实上这里的Map还是Entry

(如果set的时候原来没有值则createMap,如果get的时候没有值则取到null)

为了避免get到Null可以写一个类继承ThreadLocal然后重写initValue方法,返回一个初始值即可(ThreadLocal的是返回一个null)。

InheritableThreadLocal类

继承自ThreadLocal,这个类使得子线程可以get父线程Set的值。这里将ThreadLocal中原本应该抛异常的childValue方法重写,返回的是父线程的值。

可以修改继承的值,只要写一个类继承InheritableThreadLocal类,并重写childValue方法即可。

若在子线程get的时候父线程set了,子线程取的仍然是旧值。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JathonKatu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档