一个进程在运行态时调用sleep(),进入等待态,睡眠结束以后,并不是直接回到运行态,而是进入就绪队列,要等到其他进程放弃时间片后才能重新进入运行态。所以sleep(1000),在1000ms以后,线程不一定会被唤醒。sleep(0)可以看成一个运行态的进程产生一个中断,由运行态直接转入就绪态。这样做是给其他就绪态进程使用时间片的机会。总之,还是操作系统中运行态、就绪态和等待态相互转化的问题。
4.1 安全的终止线程
不建议使用suspend()暂停,resume()恢复,stop()停止,方法。因为:以suspend()为例,线程在调用后,并不会释放已经占有的资源(比如锁),而是占有着资源进入睡眠状态,这样很容易引发死锁问题。
安全的终止线程的方式:使用标志变量。
例如:(1)使用interrupt标志位。(2)自定义一个标志位
4.2 守护线程
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
Thread daemonTread =newThread();
// 设定 daemonThread 为 守护线程,default false(非守护线程)
daemonThread.setDaemon(true);
// 验证当前线程是否为守护线程,返回 true 则为守护线程
daemonThread.isDaemon();
所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2)在Daemon线程中产生的新线程也是Daemon的。
(3)守护线程不能访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
4.3 sleep()和wait()
sleep:(1)Thread类的方法,用于控制线程自身的执行流程。(2)调用时不需要加锁。(3)睡眠时,保持对象锁(不会释放资源)。
wait:(1)Object类方法,用于线程间通信。(2)调用时需要加锁。(3)wait调用后,会释放锁。
4.4 线程获取锁的过程
任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或者同步方法,而没有获取到监视器(执行该方法)的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED状态。
4.5 Java线程状态的变迁
yield()方法建议线程从运行态变为就绪态(即让出时间片给其他优先级相同的线程,但不能保证,因此重要的线程调度不能依赖于yield方法)。