1. 概念:
线程可以看作轻量级的进程。(进程好比一个工厂,线程好比工厂中的流水线)
进程-----线程-----协程(未来 如goLong语言)
进程是系统分配资源的最小单位,线程是系统调度资源的最小单位。 一个进程内的线程之间是可以共享资源的;每个进程至少有一个线程存在,即主线程。线程是进程实质工作的一个最小单位。
线程可以共享的资源:
(进程不可以共享资源)
- 打开的文件;
- 共享内存(对象);
线程不可以共享的资源:
- 上下文 ,记账信息,状态;
- 栈信息;
- 优先级;
面试题:
线程的数量是不是越多越好?
答:
并不是,线程过多会出现线程挣抢和CPU的过渡调用问题(理论上线程数量越多越好)。
多少线程最好?
要看具体的应用场景;对于密集型计算的CPU任务,线程数量 = CPU数量是最好的;IO(文件读写)型任务理论上线程数量越多越好。
2.线程分类:
1. 守护线程(后台线程)
2. 用户线程(默认线程)
注:
- 守护线程是用来服务用户线程的;
- 进程退出: 没有用户线程运行时,进程就会结束。
- 守护线程使用场景: Java垃圾回收器,健康检测(TCP)。
守护线程注意事项:
- 守护线程设置必须在开启线程(start)之前(如果在后则会报错,且设置的守护线程不能生效)。
- 在守护线程中创建的线程默认情况下都是守护线程。
- main函数是用户线程。
3.线程的状态:
- 新建状态(New):新创建了一个线程对象。
- 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的 start()方法。 该状态的线程位于可运行线程池中,变得可运行,等待获取 CPU 的使用权。
- 运行状态(Running):就绪状态的线程获取了 CPU,执行程序代码。
- 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止 运行。直到线程进入就绪状态,才有机会转到运行状态。
- 终止(Terminated)也称死亡状态(Dead): 线程执行完了或者因异常退出了 run()方法,该线程结束生命 周期。
观察线程的所有状态:
public class ThreadDemo23 {
public static void main(String[] args) {
// 打印所有线程转态
for (Thread.State item : Thread.State.values()) {
System.out.println(item);
}
}
}
4.线程的常见方法和属性:
方法:
方法1: 启动一个线程:
Thread.start();
面试题:run()和start()的区别?
run()方法属于普通方法,而start()方法属于启动线程的方法;run()方法可以执行多次,而start()方法只能执行一次。
- start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。
- 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
- 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运行 run 函数当中的代码;run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
方法2: 线程休眠:
休眠 1s
//方式1
Thread.sleep(1000);
//方式2
TimeUnit.SECONDS.sleep(1);
//方式3
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
方法3: 等待一个线程:
Thread.join();
注:使用该方法时要注意并行操作是不安全的,要串行操作。
// 并行操作:
t1.join();
t2.join();
t1.start();
t2.start();
//串行操作:
t1.start();
t1.join();
t2.start();
t2.join();
public class ThreadDemo22 {
public static void main(String[] args) throws InterruptedException {
// 定义公共任务
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
"上班");
try {
// 表示工作时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +
"下班");
}
};
// 定义张三
Thread t1 = new Thread(runnable, "张三");
t1.start();
// 等待张三执行完成
t1.join(1000);
// 定义李四
Thread t2 = new Thread(runnable, "李四");
t2.start();
}
}
方法4: 线程中断:
有3中方式:
- 使用全局自定义的变量来终止线程(执行时比较温和,在拿到终止指令后,会把当前的任务执行完再终止线程);
public class ThreadDemo18 {
//全局自定义变量
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
//转账线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while(!flag){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我正在转账");
}
System.out.println("终止转账!");
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
//改变变量的状态来终止线程的执行
System.out.println("终止交易,有内鬼!");
flag = true;
}
});
t2.start();
t1.join();
t2.join();
}
}
- .使用线程提供的终止方法interrupt();
对于第二种方式有两种中断方法:
public class ThreadDemo20 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.interrupted()) { // (静态方法)会进行复位
// while (!Thread.currentThread().isInterrupted()) { // (实例方法)不会复位
System.out.println("正在转账");
}
System.out.println("终止转账");
}
});
t1.start();
Thread.sleep(10);
System.out.println("有内鬼终止交易");
t1.interrupt();
}
}
注 : interrupt()方法的作用就是将线程中的终止状态从默认的false改为true。
- .使用线程提供的方法stop来终止线程。(线程不安全 该方法已废弃不用)。
方法5: 出让CPU执行权(CPU是否能成功让出,不确定):
Thread.yield();
public class ThreadDemo24 {
private static final int maxSize = 1000;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < maxSize; i++) {
// 出让 CPU 执行权
Thread.yield();
System.out.println("我是t1");
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
for (int i = 0; i < maxSize; i++) {
System.out.println("我是t2");
}
}, "t2");
t2.start();
}
}
属性:
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
注:
线程优先级的范围为[1,10],新创建的线程默认优先级是5,优先级值越大,那么他的执行权就越高(先执行)。但是:CPU的调度是很复杂的,所以不会严格的按照优先级的排序去执行,但总体来看还是优先级越高执行的权重也就越高。