本文是《java多线程编程核心技术》第三、四章的学习笔记。
线程间通信
等待/通知机制
wait()
和notify()
: wait
使线程停止运行,notify
使停止的线程继续运行。
注意事项:
wait()
自动锁放对象锁;notify()
不释放;它们必须存在与同步块中。- wait 后线程会进入等待池,需要由同一对象的 notify 方法唤醒。
- 当线程处于wait状态时,调用
interrup()
方法会出现异常。 - notify()随机唤醒线程,
notifyAll()
唤醒全部wait线程. wait(n)
,使线程等待一段时间,如果没被唤醒,超过时间自动唤醒。
可以用wait()
和notify()
实现生产者和消费者
- 假死:所有线程都呈WAITING状态。
- 原因:notify唤醒的是异类,如“生产者”唤醒“生产者”。
- 解决:将
notify()
改为notifyAll()
通过管道进行线程间通信:一个线程发送数据到输出管道,另一个线程从输入管道中读数据。
- 字节流:
PipedInputStream
和PipedOutputStream
- 字符流:
PipedReader
和PipedWriter
方法join的使用
join()
:使当前线程堵塞,等待线程对象销毁后,再执行当前线程后面的代码。
join()
与interrupt()
方法如果彼此遇到,则会出现异常。join(long)
可设定等待的时间。注意:join(long)
内部使用wait(long)
实现,所以join(long)
会释放锁;而Thread.sleep(long)
不会释放锁。
ThreadLocal的使用
ThreadLocal
类:让每个线程可以绑定自己的值- 解决
get()
返回null的问题:覆盖initialValue()
方法使变量具有初始值。 InheritableThreadLocal
类可在子线程中取得父线程继承下来的值。- 还可以对子线程继承下来的值进行修改。
Lock的使用
使用ReentrantLock类
基本用法:
- 同步:
lock()
让线程持有了“对象监视器”,和synchronized一样,线程之间还是顺序执行的。 - 等待/通知:
condition.await()
等待;condition.siganl()
通知。注意调用condition.await()
前须先调用lock.lock()
。它比wait更灵活,因为它可以用不同的condition
对象实现通知部分线程。
公平锁和非公平锁
- 公平锁:
new ReentranLock(true)
获取锁是先进先得的 - 非公平锁:
new ReentranLock(false)
获取锁是随机的
一些方法:
int getHoldCount()
查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。int getQueueLength()
返回正在等待获取此锁定的线程估计数int getWaitQueueLength(Condition condition)
返回等待与此锁定相关的给定条件Conditon的线程估计数boolean hasQueueThread(Thread thread)
查询指定的线程是否正在等待获取此锁定boolean hasQueueThreads()
查询是否有线程正在等待获取此锁定boolean hasWaiters(Condition)
查询是否有线程正在等待与此锁定有关的condition条件boolean isFair()
判断是不是公平锁boolean isHeldByCurrentThread()
查询当前线程是否保持此锁定boolean isLocked()
查询此锁定是否由任意线程保持void lockInterruptibly()
如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常boolean tryLock()
仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定boolean tryLock(long timeout,TimeUnit unit)
如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定void awaitUninterruptibly()
await状态时调用thread.interrupt()不会报错
使用ReentrantReadWriteLock类
ReentrantLock
类完全互斥,即同一时间只有一个线程可执行lock后面的任务。
ReentrantReadWriteLock
特点:
- 读读共享
- 写写互斥
- 写读互斥
- 读写互斥