2020-06-07
2020-06-07 本文已影响0人
kaikaifly
[TOC]
进程和线程的区别
- 进程:每个进程都有独立的代码和数据空间,进程间的切换会有较大的开销,一个进程可以包含n个线程(进程是资源分配的最小单元)
- 线程:同一类线程共享代码和数据空间,每个线程都有独立的运行栈和程序计数器,线程切换开销小(线程是cpu调度的最小单元)
开启线程的三种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
public class CallableThreadTest implements Callable<Integer> {
public static void main(String[] args) {
CallableThreadTest callableThreadTest = new CallableThreadTest();
FutureTask<Integer> futureTask = new FutureTask<>(callableThreadTest);
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
if (i == 20) {
new Thread(futureTask, "有返回值的线程").start();
}
}
try {
System.out.println("子线程的返回值:" + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception {
{
int i = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
}
}
}
//Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实际在就是在操作系统中启动了一个进程。
线程状态转换
thread.png- 初始状态:新建线程对象
- 就绪状态:调用start()方法,等待获取cpu的使用权
- 运行状态:就绪状态获取cpu,执行程序代码
- 阻塞状态:线程因为某些原因放弃cpu的使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态,
- 死亡状态:线程执行完毕,或者因异常退出run()方法,该线程结束生命周期
Thread.sleep()
使线程进入阻塞状态,等待sleep超时则进入就绪状态,而后等待cup资源恢复到运行状态,睡眠期间并不会释放所持有的对象锁,被其他对象调用它的interrupt()方法会产生InterruptExceptin异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有 finally语句块)以及以后的代码 。
thread.join()
thread.join()方法只会使主线程进入等待池并等待t线程执行完毕后才会被唤醒。并不影响同一时刻处在运行状态的其他线程
class Thread1 extends Thread {
private String name;
public Thread1(String name) {
super(name);
this.name = name;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!");
for (int i = 0; i < 5; i++) {
System.out.println("子线程" + name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程运行结束!");
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主线程运行开始!");
Thread1 mTh1 = new Thread1("A");
Thread1 mTh2 = new Thread1("B");
mTh1.start();
mTh2.start();
try {
mTh2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "主线程运行结束!");
}
}
// main主线程运行开始!
// A 线程运行开始!
// B 线程运行开始!
// 子线程A运行 : 0
// 子线程B运行 : 0
// 子线程A运行 : 1
// 子线程B运行 : 1
// 子线程A运行 : 2
// 子线程B运行 : 2
// 子线程B运行 : 3
// 子线程A运行 : 3
// 子线程B运行 : 4
// B 线程运行结束!
// 子线程A运行 : 4
// main主线程运行结束!
// A 线程运行结束!