线程基础
基本概念
程序是操作系统能运行的文件
进程是运行的程序
一个程序可以有多个进程
进程:进程是操作系统进行资源分配的基本单位
多进程并行处理:把程序写在不同的内存位置上来回切换
多线程并行处理:一个程序内部不同任务的来回切换
通俗来讲:
线程:程序里不同的执行路径
mian方法开启的线程叫做主线程
线程:调度执行的基本单位
- 一个程序执行的时候,内存会先找到它的主线程,推送给CPU执行,主线程执行时可能会开启其他线程,这时就涉及到线程之间的切换
线程切换也是需要资源的
面试题
单核CPU设定多线程是否有意义?
有意义
CPU密集型:计算多,IO密集型:输入输出多
工作线程数是不是设置的越大越好
不是,线程切换也需要消耗资源
工作线程数(线程池中线程数)设为多少最合适?
N = N(CPU)+ U(CPU)* (1 + W / C)
N(CPU)是处理器核的数目
U(CPU)是期望的CPU利用率(0 - 1)
W / C 是等待时间与计算时间的比率
线程的创建
创建线程的5种方法
new MyThread().start() MyThread扩展了Thread类,重写run方法
new Thread(new r()).start() r继承了Runnable接口,重写run方法
new Thread(lamda).start() 使用lamda表达式
1
2
3new Thread(()->{
System.out.println("Hello Lambda");
}).start()ThreadPool 线程池 Executors.newCachedThrad
1
2
3
4ExecutorService service = Executors.newCachedThreadPool();
service.execute(()->{
System.out.println("Hello ThreadPool");
})Future Callable and FutureTask
Callable 与 Runnable很类似,但继承Callable时,可以通过泛型指定返回类型,在通过线程池得到,用Future
接收 1
2Future<String> f = service.submit(new MyCall());
String s = f.get();FutureTask类型
1
2
3
4FutureTask<String> task = new FutureTask<>(new MyCall());
Thread t = new Thread(task);
s.start();
System.out.println(task.get());
线程的方法
sleep():当前线程暂停一段时间。
yield():退出CPU,进入等待队列(返回就绪状态)
join():t2线程里使用t1.join(),t1线程加入t2线程中,等t2执行完,再执行t1。经常用来等待另一个线程结束
线程的状态*
小节说明:
- 本节重要程度:中 (帮助理解线程问题,保障知识完整性,面试很少考)
- 本节难度:低
Java线程的6种状态:
- NEW:线程刚刚被创建还没有启动
- RANNABLE:可运行状态
- WAITING:等待被唤醒
- TIMED WAITING:隔一段时间后自动唤醒
- BLOCKED:被阻塞,正在等待锁
- TERMINATED:线程结束
每个线程只能start一次
lock方法用的是JUC的CAS上锁,忙等待,不会陷入BLOCKED状态,而是WAITING
线程的打断*
- interrupt():实例方法,设置线程的中断标志位,线程自己决定是否中断
- isInterrupted():实例方法,有没有人打扰我?
- interrupted():静态方法,有没有人打扰我(当前线程)?复位!
1 | //Thread.java |
线程使用sleep(),wait(),jion()这些方法时,会设置中断标志位,同时,也会抛出interruptedException异常,在抛出这个异常的时候,可以做某些操作,同时也会重置标志位。
interrupt()不能打断正在竞争锁的线程synchronized()和lock()
如果想打断正在竞争锁的线程,使用ReentrantLock的lockInterruptibly()
1 | /** |
线程的结束
面试题:
如何优雅的结束一个线程?
e.g.上传一个大文件,正在处理费时的计算,如何优雅的结束这个线程?
结束线程的方法:
- 自然结束(能自然结束就尽量自然结束)
- stop() suspend() resume(),现在已经不推荐用了,可能会导致数据的不一致
- volatile标志
- 不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
- 打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者,
但是,由于volatile同步线程标志位的时间控制不是很精确,有可能生产者还继续生产一段儿时间
- interrupt() and isInterrupted(比较优雅)
1 | public static void main(String[] args){ |