1. run start
run方法不能由创建的线程对象调用,达不到多线程目的。只能顺序执行。
调用start方法,即由jvm调用run方法来达到多线程目的。才能并发执行。
2.Thread Runnable Callable
Thread:继承该类;支持多线程。
Runable:实现该接口;支持多线程;无返回值;run()方法不允许抛出异常,只能catch。
Callable:实现该接口;支持多线程;有返回值;call()方法允许抛出异常。
@Override
public Integer call() throws Exception{
int rand = (int)(Math.random() * 900000) + 100000;
logger.info("线程start:" + Thread.currentThread().getName() + "| rand = " + rand);
// try {
Thread.sleep(1000L);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
logger.info("线程end:" + Thread.currentThread().getName() + "| rand = " + rand);
return rand;
}
3.线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(new MyThread1());
executorService.shutdown();
4.线程状态转换
新建状态new:创建线程对象;
就绪状态runnable:线程对象调用start()方法。除了cpu,其他运行资源均已获得;
运行状态running:已获得cpu资源,开始执行程序;
阻塞状态blocked:线程因某种原因(时间片到了,或sleep()、join())放弃cpu,进入就绪状态;
死亡状态dead:线程执行完或者因异常退出run()方法,该线程结束生命周期。
阻塞的情况分三种:
(1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,
(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
(3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
运行状态下的线程走向:
(1)、run()方法或main()方法结束后,线程就进入终止状态;
(2)、当线程调用了自身的sleep()方法或其他线程的join()方法,进程让出CPU,然后就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资源即调用sleep ()函数后,线程不会释放它的“锁标志”。)。当sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配CPU时间片。典型地,sleep()被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止;
(3)、线程调用了yield()方法,意思是放弃当前获得的CPU时间片,回到就绪状态,这时与其他进程处于同等竞争状态,OS有可能会接着又让这个进程进入运行状态; 调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间片从而需要转到另一个线程。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。即如果没有同等优先权的线程,那么Yield() 方法将不会起作用。
(4)、当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被synchroniza(同步),获取不到锁标记,将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入就绪状态,等待OS分配CPU时间片;
(5)、suspend() 和 resume()方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume()被调用,才能使得线程重新进入可执行状态。典型地,suspend()和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume()使其恢复
(6)、wait()和 notify() 方法:当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。
wait()有两种形式:一种有参(单位:毫秒);一种无参。前者当对应的 notify()被调用 或 超出指定时间时线程重新进入可执行状态即就绪状态,后者则必须对应的 notify()被调用。
5.区别、释义
wait() sleep():
wait()是object类的方法。会放弃锁标识;因此必须在synchronized函数或synchronizedblock中进行调用,该方法或块的上锁对象就是调用这一对方法的对象。与此相同的是notify()。
sleep()是thread类的方法。不会放弃锁标识;因此无须在同步代码块中进行调用。
一个线程结束的标志是:run()方法结束。
一个机锁被释放的标志是:synchronized块或方法结束。
yield():停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield() 方法将不会起作用。
join():让父线程等待子线程结束之后才能继续运行。
suspend()、resume():API已过期。不推荐使用 suspend() 去挂起线程,因为 suspend() 在导致线程暂停的同时,并不会去释放任何锁资源。其他线程都无法访问被它占用的锁。如果 resume() 操作出现在 suspend() 之前执行,那么线程将一直处于挂起状态,同时一直占用锁,这就产生了死锁。而且,对于被挂起的线程,它的线程状态居然还是 Runnable。
suspend()方法和不指定超时期限的wait()方法的调用都可能产生死锁。遗憾的是,Java并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。