线程同步
线程状态
-
新建(new):新创建线程
- 继承Thread
- 实现Runnable 或者Callable接口
- 可运行(Runnable):线程对象创建后,其他线程(如main线程)调用该线程对象start方法,使其位于可运行线程池中,等待cpu调度
- 运行中(running):可运行状态线程获取cpu时间片,执行程序代码
-
阻塞(block):线程因某种原因出让了cpu时间片,暂时停止运行,阻塞的情况分三种:
- 等待阻塞:运行线程执行o.wait()方法,jvm将其放入等待队列中,直到其被其他线程唤醒(调用o.notify()或者o.notifyAll()方法), 此状态下线程会释放锁
- 同步阻塞:运行线程竞争获取对象锁时,若该对象锁被别的线程占用, 则jvm会把该线程放入锁池(lock pool)中
- 运行线程执行Thread.sleep或 t.join()方法,或者发出了I/O 请求时,jvm会把该线程置为阻塞。当 sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行状态
- 销亡(dead):线程 run()、 main()方法执行结束,或者因异常退出了run()方法,则该线程结 束生命周期。死亡的线程不可再次复生
线程安全
当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程讲如何交替执行,并且在主线程中不需要做额外的同步或协调,这个类都能表现出正确的行为,那么就程这个类是线程安全的。
线程安全实现策略
1. 线程封闭:线程封闭的对象只能有一个线程拥有,对象被封闭在线程中,并且只能由这个 线程修改
2.只读共享:在没有额外的同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改他。共享的只读对象包括不可变对象和事实不可变对象。
3.线程安全共享:线程安全的对象在其内部实现同步,因此多个线程可以通过对象的共有接口 进行访问而不需要进一步的同步
4.保护对象:被保护的对象只能通过持有特定的锁来访问。保护对象包括封装在其他的线程安全的对象中的对象,以及已发布的并且由某个特定锁保护的对象。
无状态对象一定是线程安全的
不可变对象一定是线程安全的
静态工具类的线程安全??
工具类能否设计成单例?如果能最好。单例能减少创建类和分配内存的开销,减少垃圾回收次数。
工具类能否设计成不变类?如果能最好,不变类天生线程安全!
线程中断
- interrupt()只是改变中断状态而已. interrupt()不会中断一个正在运行的线程。这一方法实际上完成的是,给受
阻塞的线程抛出一个中断信号,这样受阻线程就得以退出阻塞的状态。更确切 的说,如果线程被Object.wait,
Thread.join和Thread.sleep三种方法之一阻塞, 那么,它将接收到一个中断异常(InterruptedException),
从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到InterruptedException
异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态
2.interrupted()方法会检查当前线程的中断状态,如果为 "被中断状态 "则改变当前线程为 "非中断状态 "并返回
true,如果为 "非中断状态 "则返回false,它不仅检查当前线程是否为中断状态,而且在保证当前线程状态恢复为非中
断状态,所以它叫 "interrupted ",是说中断的状态已经结束(到非中断状态了)
3.isInterrupted()方法则仅仅检查线程对象对应的线程是否是中断状态,并不改变它的状态.
interrupt 中断操作时,非自身打断需要先检测是否有中断权限,这由jvm的安全机制配置;
如果线程处于sleep, wait, join 等状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常;
如果线程处于I/O阻塞状态,将会抛出ClosedByInterruptException(IOException的子类)异常;
如果线程在Selector上被阻塞,select方法将立即返回;
如果非以上情况,将直接标记 interrupt 状态;
注意:interrupt 操作不会打断所有阻塞,只有上述阻塞情况才在jvm的打断范围内,如处于锁阻塞的线程,不会受 interrupt 中断;