本文是《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特点:
- 读读共享
- 写写互斥
- 写读互斥
- 读写互斥