多线程的使用场景?
(1)提高性能,效率(并行并发执行)
(2)阻塞代码导致后续代码无法执行,多线程让后续代码可以执行,不受阻塞代码的影响
什么因素会影响多线程的效率?
根据哪些因素设计多线程的数量?
Thread常用API
静态方法:作用在当前线程
static int | activeCount() 获取当前线程主中还存活的线程数量 |
---|---|
static Thread | currentThread() 获取代码行所在的当前线程 |
static boolean | interrupted() 中断线程 |
static void | sleep(long millis) 让当前线程休眠给定的时间,单位毫秒 |
staticvoid | yield() 让当前线程让步:从运行态转变为就绪态 |
实例方法:作用在调用的线程对象上
String | getName() 获取线程名称 |
---|---|
int | getPriority() 获取线程优先级,返回0 - 10的数值 |
void | interrupt() |
Thread.State | getState() |
boolean | isAlive() |
boolean | isInterrupt() |
void | join() 等待一个线程 |
void | join(long millis) |
void | run() 定义线程任务 |
void | setDaemon() 设置守护线程 |
void | setUncaughtExecptionHandler(Thread.UncaughtExecptionHandler) 设置处理异常的处理器 |
void | start() 申请系统调度运行线程 |
分析1:进程中子线程处于 TIMED-WAITING 状态,即限时等待,main线程处于RUNNABLE状态(运行态+就绪态) ,即可执行状态
public static void main(String[] args) {
for (int i = 0; i < 20;i++) { //TIMED-WAITING状态 限时等待
final int n = i;
//子线程休眠三秒之后,同时执行(无序执行,由系统调度)
Thread t = new Thread(new Runnable() {
@Override
public void run() {//内部类使用外部的变量,必须用final修饰
try{
Thread.sleep(3000);
System.out.println(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
while (true) {
System.out.println("处于RUNNABLE状态 可执行状态");
}
//该共有两个线程
}
分析2:main线程子线程同时执行,子线程休眠三秒之后会同时执行
public static void main(String[] args) {
for (int i = 0; i < 20;i++) {
final int n = i;
//子线程休眠三秒之后,同时执行(无序执行,由系统调度)
Thread t = new Thread(new Runnable() {
@Override
public void run() {//内部类使用外部的变量,必须用final修饰
try{
Thread.sleep(3000);
System.out.println(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
//main线程子线程同时执行
System.out.println("ok"); //先打印ok,后0-19随机打印
}
分析3:去掉sleep后,main线程子线程同时执行,但是t线程比较耗时
public static void main(String[] args) {
for (int i = 0; i < 20;i++) {
final int n = i;
Thread t = new Thread(new Runnable() {
@Override
public void run() {//内部类使用外部的变量,必须用final修饰
System.out.println(n);
}
});
t.start();
}
//main线程子线程同时执行
System.out.println("ok"); //随机打印
// Thread[] threads = new Thread[20];
// for (int i = 0; i < 20;i++) {
// final int n = i;
// threads[i] = new Thread(new Runnable() {
// @Override
// public void run() {//内部类使用外部的变量,必须用final修饰
// System.out.println(n);
// }
// });
// }
// for (Thread t : threads) {
// t.start();
// }
// //main线程子线程同时执行
// System.out.println("ok");
}
}
分析4:由于new Thread时较为耗时,虽然t和main同时并行并发执行,随机打印,但因为main正在运行态执行代码,所以后续代码很快执行,所以先打印main的概率较高
public static void main(String[] args) {
//new Thread较为耗时
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t");
}
}); //申请系统创建线程t
t.start(); //申请系统执行线程t,创建态转变为就绪态,由系统决定什么时候转变为运行态(耗时)
System.out.println("main");
}
yield和join的使用:
如何使程序最后打印ok?
yield的使用:让当前线程让步,从运行态转变为就绪态
while (Thread.activeCount() > 1){Thread.yield();}
join()的使用:
for (Thread t : threads) { t.start(); }
for (Thread t : threads) { t.join(); }
若 t.start(),t.join()放在同一个循环,则顺序打印0-19
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[20];
for (int i = 0; i < 20;i++) {
final int n = i;
threads[i] = new Thread(new Runnable() {
@Override
public void run() {//内部类使用外部的变量,必须用final修饰
System.out.println(n);
}
});
}
for (Thread t : threads) {
t.start();
}
//法一
for (Thread t : threads) {
t.join();
}
//法二
// while (Thread.activeCount() > 1) { //存活的线程数
// Thread.yield(); //让当前线程让步:从运行态转变为就绪态 使最后打印ok
// }
//idea会自动启动一个线程
//所以使用run 写Thread.activeCount() > 2
//使用debug 写Thread.activeCount() > 1
//否则不会打印ok
System.out.println("ok");
join():无条件等待
public static void main(String[] args) throws InterruptedException {
//new Thread稍微有点耗时
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t");
}
}); //申请系统创建线程t
t.start(); //申请系统执行线程t,创建态转变为就绪态,由系统决定什么时候转变为运行态(耗时)
t.join(); //当前线程(main线程)无条件阻塞等待,直到t线程执行完毕后再往后执行
System.out.println("main"); //先打印t,在打印main
}
join(long millis):限时等待
public static void main(String[] args) throws InterruptedException {
//new Thread稍微有点耗时
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); //等待三秒
System.out.println("t");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); //申请系统创建线程t
// t.start(); //申请系统执行线程t,创建态转变为就绪态,由系统决定什么时候转变为运行态(耗时)
// t.join(); //当前线程(main线程)无条件等待,直到t线程执行完毕后再往后执行
// System.out.println("main");
// //先等三秒,再打印t,再打印main
// t.start();
// t.join(1000); //当前线程限时等待,直到t线程执行完毕,或者等待时间结束
// System.out.println("main");
// //先等一秒打印main,在等两秒打印t
t.start();
t.join(4000); //当前线程限时等待,直到t线程执行完毕,或者等待时间结束
System.out.println("main");
//等三秒(系统调度t由就绪态变为运行态的时间 + t运行的时间)打印t,然后直接打印main
}
线程状态:
interrupt() :线程中断
模拟t执行5秒之后还没有结束,要中断,停止t线程
t收到中断通知的方法有两种:
- 若线程调用了wait/ join/ sleep等方法而阻塞挂起,则以InterruptedException异常的形式通知,清除中断标志
- 否则,只是内部的一个中断标志被设置,
t可以通过Thread.interrupted()【静态方法,返回当前线程中断标志位,然后设置中断标志位】判断当前线程的中断标志被设置,清除中断标志,
或者通过Thread.currentThread().isInterruption()
判断指定线程的中断标志被设置,不清除中断标志
自己实现:
private static volatile boolean STOP = false;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
//执行任务,执行时间较长
for (int i = 0; i < 1000 && !STOP;i++) {
System.out.println(i);
//模拟中断线程
Thread.sleep(1000);
//通过标志位自行实现无法解决线程阻塞,导致无法中断
//Thread.sleep(100000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
System.out.println("t start");
//模拟t执行5秒之后还没有结束,要中断,停止t线程
Thread.sleep(5000);
STOP = true;
System.out.println("t stop");
//先打印t start,每隔一秒打印一个数字,五秒后,打印t stop
}
使用API:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//中断以后,停止执行
// try {
// //执行任务,执行时间较长
// for (int i = 0; i < 1000 && !Thread.currentThread().isInterrupted();i++) { //没有被中断,往下执行
// System.out.println(i);
// Thread.sleep(1000);
// }
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//中断以后继续执行
for (int i = 0; i < 1000 && !Thread.currentThread().isInterrupted();i++) { //没有被中断,往下执行
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start(); //线程启动,中断标志位为false
System.out.println("t start");
//模拟t执行5秒之后还没有结束,要中断,停止t线程
Thread.sleep(5000);
t.interrupt();//告诉t线程要中断(设置t线程的中断标志位为true),由t的代码自己决定是否要中断
//如果线程处于阻塞状态,会抛出InterruptException 异常,并且重置t线程的标志位
System.out.println("t stop");
}
中断以后停止执行结果:
中断以后继续执行结果: