Java多线程之线程的控制
- 线程中的7 种非常重要的状态:
如果将“锁池”和“等待队列”都看成是“阻塞”状态的特殊情况,那么可以将线程归纳为5个状态:
新建,就绪,运行,阻塞,死亡。
┌--------------------< 阻塞
↓ (1)(2)(3) 结束
①②③ OS调度 ↑ ↑
初始-------> 可运行 ↹---------------↹ 运行 >-----------┤
t.start()启动 ↑ ↓ ↓o.wait()
└-----< 锁池 ←---------<┘←-------< 等待队列
获得锁标志 synchronized(o)
注意:图中标记依次为
①输入完毕; ②wake up ③t1 退出
⑴等待输入(输入设备进行处理,而CUP 不处理),则放入阻塞,直到输入完毕。
⑵线程休眠sleep()
⑶t1.join()将t1 加入运行队列,直到t1 退出,当前线程才继续。
特别注意:①②③与⑴⑵⑶是一一对应的。
进程的休眠:Thread.sleep(1000);//括号中以毫秒为单位
当线程运行完毕,即使在结束时时间片还没有用完,CPU 也放弃此时间片,继续运行其他程序。
T1.join 实际上是把并发的线程编成并行运行。
- 实现对多线程的控制:
1,start方法:创建一个线程。值得注意的是,new一个线程的时候,只是表示该线程处于新建状态,并不是执行状态。使用start方法后启动一个线程,线程处于就绪状态,也并不是说直接就在运行了,它表示这个线程可以运行了,但是什么时候开始运行,取决于JVM里线程调度器的调度。
2,stop方法:结束一个线程。容易导致死锁,不推荐使用。值得注意的是,不要试图在一个线程已死亡的情况下再次调用start方法来启动该线程,会报illegalThreadStateException错的。
3,join方法:在一个线程中加入一个线程。当在某个程序执行流中调用了其他线程的join方法时,调用线程将被阻塞,直到被join方法加入的join线程完成为止。
4,sleep方法:让线程睡觉。使当前正在执行的线程暂停一段时间,并进入阻塞状态。
5,yield方法:和sleep方法相似,让当前正在执行的线程暂停一下,但是他不会阻塞这个线程,它只是将这个线程转入就绪状态。
6,setPriority方法:设置线程优先级。set和getPriority设置和获得线程的优先级,1到10一共10个数字,默认是5,数字越大优先级越高,越容易获得执行机会。 为了跨平台,最好不要使用优先级决定线程的执行顺序。这个时候使用3个静态常量:max_Priority,min_Priority,norm_Priority。
7,setDaemon方法:设置后台线程。 Daemon Threads(daemon 线程)是服务线程,当其他线程全部结束,只剩下daemon线程时,虚拟机会立即退出。
Thread t = new DaemonThread();
t.setDaemon(true);//setDaemon(true)把线程标志为daemon,其他的都跟一般线程一样
t.start();//一定要先setDaemon(true),再启动线程
在daemon线程内启动的线程,都定为daemon线程
8,wait(),Notify()方法在后面介绍同步锁的时候在仔细说。 在运行状态中,线程调用wait(),表示这线程将释放自己所有的锁标记,同时进入这个对象的等待队列。等待队列的状态也是阻塞状态,只不过线程释放自己的锁标记。用notify()方法叫出之后,紧跟着刚才wait();的位置往下执行。如果一个线程调用对象的notify(),就是通知对象等待队列的一个线程出列。进入锁池。如果使用notifyall()则通知等待队列中所有的线程出列。
- 有一个问题,在什么情况下,一个线程将进入阻塞状态?
2,线程调用了阻塞式IO方法,在方法返回之前,线程阻塞了
3,线程试图获得一个同步监听器,但是该同步监视器正被其他的线程持有
4,线程在等待某一个notify通知
5,线程调用了suspend方法将线程挂起了。
要是让一个线程由阻塞状态变成了就绪状态正好和上面的情况相反,最后一点使用resume方法就好了。值得注意的是:suspend()将运行状态推到阻塞状态(注意不释放锁标记)。恢复状态用resume()。Stop()释放全部。这几个方法上都有Deprecated 标志,说明这个方法不推荐使用。一般来说,主方法main()结束的时候线程结束,可是也可能出现需要中断线程的情况。对于多线程一般每个线程都是一个循环,如果中断线程我们必须想办法使其退出。如果想结束阻塞中的线程(如sleep 或wait),可以由其他线程对其对象调用interrupt()。用于对阻塞(或锁池)会抛出例外Interrupted。