核心概念:
(1)进程包含线程
(2)进程是系统资源分配的最小单位;
线程是系统调度执行的最小单位;
一.创建线程的方法
(1)继承Thread,重写run.通过Thread实例start启动线程;
public class demo1 {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
while(true) {
System.out.println("hello main!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(2)实现Runnable,重写run.通过Thread的实例,把Runnable的实例传进去,再调用start;
class MyRunnable implements Runnable{
@Override
public void run() {
while(true) {
System.out.println("hello thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class demo2 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t = new Thread(myRunnable);
t.start();
while(true) {
System.out.println("hello main!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(3)通过匿名内部类的方式来编写,本质上是(1);
public class demo3 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
System.out.println("hello thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
while(true) {
System.out.println("hello main!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(4)通过匿名内部类的方式来编写,本质上是(2);
public class demo4 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("hello Thread!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
while(true) {
System.out.println("hello main!");
Thread.sleep(1000);
}
}
}
(5)基于lambda表达式创线程;(推荐)
public class demo5 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while(true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
while(true) {
System.out.println("hello main");
Thread.sleep(1000);
}
}
}
注意:
(1)代码中创建的new Thread对象的生命周期和内核中实际的线程是不一定一样的,
可能会出现Thread对象存在但是内核中的线程不存在的情况,但是不会存在相反的情况;
(2)start和run的差别:
run:描述了线程要执行的任务,也可以称为线程的入口;
start:调用系统函数,真正在系统内核中,创建线程(创建PCB,加入到链表中),会根据不同的系统调用不同的api,创建好线程之后再来执行run;
(3)不一定非得main线程调用其他线程,其他线程也能调用新线程;
(4)一个Thread对象只能调用一次start;(一个对象只能对应一个线程);
如果Thread对象没有start,此时状态为NEW状态,若start过后则为非NEW状态;
补充概念:
变量捕获:将变量和lambda表达式定义在同一作用域当中,此时lambda内部是可以访问到lambda外部(和lambda同一作用域)的变量的;
Java中变量捕获的特殊要求(C++,JS等语言中没有这种要求):捕获的变量得是final/事实final;
lambda表达式本质上是一个"函数式接口"产生的匿名内部类;
二.前台线程和后台线程
概念
后台线程(deamon守护线程):在线程执行过程中,不能阻止进程结束(即进程结束时,线程也会随之结束);
前台线程:在线程执行过程中,可以阻止进程结束;
注意:
(1)进程(前台线程)要结束,后台线程无力阻止;
(2)后台线程先结束了,也不影响前台线程的结束;
(3)一个进程中,可以有多个前台线程;
多线程的使用场景:
(1)CPU密集型
(2)IO密集型
三.中止线程
(1)通过共享的标记来进⾏沟通
public class demo6 {
private static boolean isQuit = false;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while(!isQuit) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(5000);
System.out.println(t.isAlive());
isQuit = true;
Thread.sleep(1000);
System.out.println(t.isAlive());
}
}
(2)调⽤ interrupt() ⽅法来通知
注意:(1)当interrupt()方法被执行时会直接唤醒sleep();
(2)当sleep()被唤醒时,会清空标志位;
(3)因此想要结束循环必须在catch中加上break或者return语句;