这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
引子
线程的历史--一部对于CPU性能压榨的历史。
什么是程序?进程?线程?纤程/协程?
程序--是指可执行文件,比如QQ、微信。
进程--程序运行起来就产生了进程,一个程序可以多次运行产生多个进程,进程是系统进行资源分配的基本单位。(静态概念)
线程--一个进程里可以有多个任务并行执行,就产生了多个线程,线程是调度执行的基本单位,多个线程共享一个进程里面的资源。(动态概念)
线程方面的问题
1.单核CPU设定多线程是否有意义?
答:有意义。并不是所有的线程操作都是需要消耗CPU,有些可能在sleep,有些可能在进行IO操作。
2.工作线程数是不是设置的越大越好?
答:不是,因为线程之间的切换是需要消耗系统资源的。
3.工作线程数(线程池中线程数量)设多少合适?
答:测试环境通过性能分析工具profiler用来测算 等待时间 和 计算时间,生产环境可以使用Arthas。
创建线程的5种方式
- 继承Thread类
- new MyThread().start()
- 实现Runnable接口->run()(无返回值,好处是可以继承其它类)
- new Thread(r).start()
- 实现Callable接口->call()(有返回值)
- Future Callable and FutureTask
- 使用lambda表达式作为入参
- new Thread(lamda).start()
- 线程池来创建Executors
- ThreadPool 代码演示
package com.mashibing.juc.c_000_threadbasic;
import java.util.concurrent.*;
public class T02_HowToCreateThread {
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello MyThread!");
}
}
static class MyRun implements Runnable {
@Override
public void run() {
System.out.println("Hello MyRun!");
}
}
static class MyCall implements Callable<String> {
@Override
public String call() {
System.out.println("Hello MyCall");
return "success";
}
}
//启动线程的5种方式
public static void main(String[] args) throws Exception {
new MyThread().start();
new Thread(new MyRun()).start();
new Thread(() -> {
System.out.println("Hello Lambda!");
}).start();
FutureTask<String> task = new FutureTask<>(new MyCall());
Thread t = new Thread(task);
t.start();
System.out.println(task.get());
ExecutorService service = Executors.newCachedThreadPool();
service.execute(() -> {
System.out.println("Hello ThreadPool");
});
Future<String> f = service.submit(new MyCall());
String s = f.get();
System.out.println(s);
service.shutdown();
}
}
JAVA的6中线程状态
- NEW :线程刚刚创建,还没有启动
- RUNNABLE :可运行状态,由线程调度器可以安排执行
- 包括READY和RUNNING两种细分状态
- WAITING: 等待被唤醒
- TIMED WAITING: 隔一段时间后自动唤醒
- BLOCKED:被阻塞,正在等待锁
- TERMINATED:线程结束
线程的打断(interrupt)
- interrupt()
- 打断某个线程(设置标志位)
- 实例方法,设置线程中断标志(打扰一下,你该处理一下中断)
- isInterrupted()
- 查询某线程是否被打断过(查询标志位)
- 实例方法,有没有人打扰我?
- static interrupted()
- 查询当前线程是否被打断过,并重置打断标志
- 静态方法,有没有人打扰我(当前线程)?复位!
//Thread.java
public void interrupt() //t.interrupt() 打断t线程(设置t线程某给标志位f=true,并不是打断线程的运行)
public boolean isInterrupted() //t.isInterrupted() 查询打断标志位是否被设置(是不是曾经被打断过)
public static boolean interrupted()//Thread.interrupted() 查看“当前”线程是否被打断,如果被打断,恢复标志位
程序在sleep、wait、join的时候,如果此时设置了标志位interrupt(),会被唤醒,然后抛出InterruptedException异常,catch之后默认会把标志位重置为false
代码演示
/**
* interrupt与sleep() wait() join()
*/
public static void main(String[] args) {
Thread t = new Thread(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {//catch之后默认会把标志位重置为false
System.out.println("Thread is interrupted!");
System.out.println(Thread.currentThread().isInterrupted());
}
});
t.start();
SleepHelper.sleepSeconds(5);
t.interrupt();
}
通常情况下,线程在竞争锁的过程中是不能被打断的
synchronized与interrupt()
public class T09_Interrupt_and_sync {
private static Object o = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (o) {
SleepHelper.sleepSeconds(10);
}
});
t1.start();
SleepHelper.sleepSeconds(1);
Thread t2 = new Thread(() -> {
synchronized (o) {
}
System.out.println("t2 end!");
});
t2.start();
SleepHelper.sleepSeconds(1);
t2.interrupt();
}
}
lock与interrupt()
public class T10_Interrupt_and_lock {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
lock.lock();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
System.out.println("t1 end!");
});
t1.start();
SleepHelper.sleepSeconds(1);
Thread t2 = new Thread(() -> {
lock.lock();
try {
} finally {
lock.unlock();
}
System.out.println("t2 end!");
});
t2.start();
SleepHelper.sleepSeconds(1);
t2.interrupt();
}
}
ReentrantLock的例外lockInterruptibly()
public class T11_Interrupt_and_lockInterruptibly {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
lock.lock();
try {
SleepHelper.sleepSeconds(10);
} finally {
lock.unlock();
}
System.out.println("t1 end!");
});
t1.start();
SleepHelper.sleepSeconds(1);
Thread t2 = new Thread(() -> {
System.out.println("t2 start!");
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
System.out.println("t2 end!");
});
t2.start();
SleepHelper.sleepSeconds(1);
t2.interrupt();
}
}
面试题:如何优雅的结束一个线程?
eg:上传一个大文件,正在处理费时的计算,如何优雅的结束这个线程?
- 自然结束(能自然结束就尽量自然结束)
终止:stop() 、暂停:suspend() 、恢复:resume()- volatile标志
- 不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
- 打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者,但是,由于volatile同步线程标志位的时间控制不是很精确,有可能生产者还继续生产一段儿时间
private static volatile boolean running = true;
public static void main(String[] args) {
Thread t = new Thread(() -> {
long i = 0L;
while (running) {
//wait recv accept
i++;
}
System.out.println("end and i = " + i); //4168806262
});
t.start();
SleepHelper.sleepSeconds(1);
running = false;
}
-
interrupt() and isInterrupted、interrupted()(比较优雅)
但也不能精准地控制结束的次数和时间
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.interrupted()) {
//sleep wait
}
System.out.println("t1 end!");
});
t.start();
SleepHelper.sleepSeconds(1);
t.interrupt();
}