1.进程与线程
1.进程是资源分配的最小单位,线程是CPU调度的最小单位。
2.进程可以看作独立应用,线程不能看作独立应用。
3.进程独占内存空间,保存各自的运行状态,相互之间互不干扰且可以互相切换。线程共享进程的内存资源,相互之间切换更加快捷,支持更细粒度的任务控制
4.多进程的程序比多线程的程序更加健壮,但是进程的切换开销比线程大
2. run和start
start()方法的执行流程:
- 检查线程状态是不是NEW(线程刚创建未启动的状态),如果不是抛出异常(如果调用多次start方法会抛出IllegalThreadStateException的异常)
- 加入线程组
- 调用native的start0方法
而run方法只是一个普通的方法。只有调用start方法启动线程才算是真正的启动线程,线程才会有生命周期。
3.Thread类的方法
特别要注意的是run()
和exit()
都是Thread类的方法。虽然run()在Runnable接口中也有。
此外,Thread类有一个native的notify/notifyAll方法,但是没有wait方法。(注意wait/notify/notifyAll都是Object的方法)
4.线程返回值处理
1.主线程等待法(while循环等待),等待子线程执行完成返回结果
2.使用Thread类的join方法阻塞当前线程,等待子线程执行完成返回结果
3.实现Callbale接口实现,通过FutureTask或线程池submit之后接收的Futre。FutureTask/Future的get()会阻塞直到子线程执行完成返回结果。如果子线程发生异常且没有捕获,get()不再阻塞。
4.1 主线程等待法
1 | public class ThreadTest { |
4.2 使用join()
使用4.1中的MyThread,改变main方法:
1 | public static void main(String[] args) throws ExecutionException, InterruptedException { |
4.3 通过FutureTask接收返回值
1 | public class ThreadTest { |
4.4 通过线程池和Futrue接收返回值
1 | public class ThreadTest { |
5.notify/notifyAll
锁池:两个线程竞争同一把锁,没有竞争到锁的线程将进入锁池
。锁池可以竞争锁。
等待池:在等待池的线程等待着被唤醒进入锁池竞争锁。
1.notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会。notifyAll会让所有处于等待池中的线程全部进入锁池中去竞争获取锁的机会。
1 | public class ThreadTest { |
6.停止线程
错误的停止方法:
1.stop:stop非常粗暴,被stop的线程会释放所有的锁并停止线程执行,即使它并没有执行完成。stop不能完成一个完整单位的停止。
2.resume:resume会将当前的线程挂起,但并没有释放锁。这有可能会导致其他需要这把锁的线程发生死锁。
3.volatile修饰的成员变量控制停止:在某些情况下,volatile修饰的成员变量去控制线程的停止是可行的。但在线程阻塞的时候,线程有可能检测不到成员变量的改变,导致线程停止失败。
例子中,生产者往queue中生产数据,消费者从queue中消费数据。当消费者消费完成后,希望生产者停止线程。
使用volatile修饰的canceled变量控制线程的停止,当生产者的线程阻塞时,无法检测到外界canceled变量的改变,而无法正确停止线程。
1 | public class ThreadTest { |
正确的停止方法:
使用Thread.interrupt停止线程
将上面的例子修改为使用interrupt来停止线程,当调用interrupt方法时,isInterrupted方法可以检测到线程的中断。
1 | public class ThreadTest { |