本片学习多线程的几种创建方式
文章目录
线程的创建
1、继承 Thread 来创建一个线程类
Java标准库提供了一个类Thread能表示一个线程
实现步骤
-
继承 Thread 来创建一个线程类.重写run方法
-
创建 MyThread 类的实例
-
调用 start 方法启动线程
class MyTherad extends Thread{
@Override
public void run() {
System.out.println("hello");
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
Thread t = new Thread();//先创建MyThead实例,t的引用实际指向子类的实例
t.start();//启动线程,在进程中搞了另一个流水线,新的流水线开始并发执行另提供逻辑
}
}
。
上述代码涉及到了两个线程
-
main方法对应的线程(一个进程至少得有一个线程)也可称为主线程
-
通过t.start创建的新线程
使用Tread的run描述线程入口
t.start会创建一个新的线程并执行
t.run()会执行里面的线程
run不是一个随便的方法,重写了父类的方法,
这种重写一般称为功能的扩展,不需要手动调用
2、实现使用 Runnable 接口
Runnable描述一个具体的任务
通过run方法重写
使用Runnable interface 来描述线程入口
实现步骤
- 实现 Runnable 接口
- 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.。
- 调用 start 方法
class MyRunable implements Runnable{
@Override
public void run() {
while (true){
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
MyRunable runable = new MyRunable();
Thread t = new Thread(runable);
t.start();
while (true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
使用Runnable interface 来描述线程入口
继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()
3、匿名内部类创建 Thread 子类对象
public class ThreadDemo3 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunable(){
@Override
public void run() {
while (true){
System.out.println("hello t");
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、匿名内部类创建 Runnable 子类对象
public class ThreadDemo4 {
public static void main(String[] args) {
Thread t =new Thread(){
@Override
public void run() {
while (true){
System.out.println("hello t");
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();
}
}
}
}
5、lambda 表达式创建 Runnable 子类对象
语法规则:
public static void main(String[] args) {
Thread t = new Thread(() ->{
});
}
()里面放参数,如果只有一个参数,()可以省略,{}里面放函数体、代码
Thread类中的方法
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
java代码中的Thread对象和操作系统中的线程是一一对应的。
- ID :是线程的唯一标识,不同线程不会重复
- name:名称是各种调试工具用到
- state: 状态表示线程当前所处的一个情况,下面我们会进一步说明
- priority :优先级高的线程理论上来说更容易被调度到
- daemon :关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- alive: 是否存活,即简单的理解,为 run 方法是否运行结束了
- 线程的中断问题
true 表示后台线程
false表示前台线程
前台线程会组织Java线程结束
必须得java线程中所以的前台线程都执行完,Java线程才能结束
创建的线程默认是前台的,可以通过setDeamon设置为后台的
6.实现Callable
类似Runnable :描述了一个任务,一个线程干什么,通过run方法返回,返回值类型void
callable 方法有返回值
public class ThreadDemo27 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//这只是创建一个任务
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {//这个泛型类型是什么,call方法的返回值类型就是什么
int sum = 0;
for (int i = 0; i <= 10050; i++) {
sum += i;
}
return sum;
}
};
//Thrad不能直接传callable,需要包装一层
FutureTask<Integer> futureTask = new FutureTask<>(callable);// FutureTask未来要执行的任务
Thread t = new Thread(futureTask);
t.start();
System.out.println(futureTask.get());/*此处get就是获取上述任务call方法返回值的结果,call方法是t线程的,get方法是主线程的
如何保证get的时候,t线程执行完了,get和join类似都会阻塞等待*/
}
}
线程的使用
线程的启动
start 方法:真正从系统这里,创建一个线程,新的线程将会执行run方法
调用 start 方法, 才真的在操作系统的底层创建出一个线程.
run方法:表示线程的入口方法是什么(线程启动起来,要执行哪些逻辑)
线程的中断
线程的终止
本质上来说,让一个线程终止,办法只有一个,结束该线程的入口方法执行完毕(return,抛出异常)
方法:
1.给线程设定一个结束标志位
public class ThreadDemo9 {
public static boolean isQuit = false;
public static void main(String[] args) {
public static boolean isQuit = false;//区别
Thread t =new Thread(()->{
while (!isQuit){
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t,线程终止");
});
t.start();
//在主线程中,修改isQit
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isQuit = true;
}
}
lambda 表达式能否访问外部变量
Java要求变量捕获,捕获的变量必须是final或者“实际的final”(变量没有被final修饰,但代码中没有做出修改)
package threading;
public class ThreadDemo10 {
public static void main(String[] args) {
Thread t = new Thread(()->{
//currentThread是获取当前线程的实例
//此处 currentThrerad得到的就是t
//isInterruptead就是t对象里自带的一个标志位
while (!Thread.currentThread().isInterrupted()){
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();//打印出当前异常位置的调用栈
}
//把t内部的标志位设置位true,,,默认的是false
t.interrupt();//1.设置标志位为true,2.如果该线程正在阻塞(比如执行sleep,此时会把阻塞状态唤醒,通过抛异常的方式让sleep立即结束
//当sleep被唤醒,sleep会自动把isInterrupted的标志位清空(true->false),,,目的就是为了让线程自身能够对线程何时结束,有一个明确的控制
}
}
三种方法,1.无视 2.立即 3. 等待
interrupt方法效果,不是让线程立即结束,而是告诉你线程该结束了,只是通知而不是命令
搞个break
线程的等待
join方法
调用谁,等待他等待
阻塞//走到这停下,当前这个线程暂时不参与cpu的调度
1.main线程调用t.join的时候,如果t还在运行,此时main线程阻塞,直到t执行完毕,main才从阻塞中解除,才继续执行
2.main线程调用t.join的时候,如果t已经结束了,此时join不会阻塞,就会立刻往下执行难,保证join先执行
join两个版本:无参版本(死等) 有参版本,指定最大等待时间,如果等待时间达到了上限,就不等了
获取当前线程的引用
public static Thread currentThread(); 返回当前线程对象的引用
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
休眠当前线程
使用Thread类的sleep()方法。
sleep()方法有两种重载形式:
- static void sleep(long millis):使当前线程休眠指定的毫秒数。
- static void sleep(long millis, int nanos):使当前线程休眠指定的毫秒数和纳秒数。
代码示例
public class SleepThreadExample {
public static void main(String[] args) {
System.out.println("Main thread starts...");
// 创建并启动新线程
Thread childThread = new Thread(() -> {
try {
System.out.println("Child thread starts...");
Thread.sleep(3000); // 休眠3秒
System.out.println("Child thread resumes...");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
childThread.start();
// 主线程继续执行
System.out.println("Main thread continues...");
try {
childThread.join(); // 等待子线程执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread ends.");
}
}
线程的状态
NEW: 安排了工作, 还未开始行动
RUNNABLE: 就绪状态/可工作的. 又可以分成正在CPU上运行和准备好随时可以去CPU上运行
- Ready:线程在就绪队列上等待调度
- Running: 线程在CPU上运行
BLOCKED: 这几个都表示排队等着其他事情 //表示等待锁出现的状态
WAITING: 这几个都表示排队等着其他事情 //表示wait方法数出现的状态
TIMED_WAITING: 这几个都表示排队等着其他事情//指定时间等待
TERMINATED: 工作完成了.
public class ThreadDemo12 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while (true){
//为了防止hello把线程状态冲没了,先注释掉
//System.out.println("hello");
}
});
//在启动前,获取进程状态 NEW
System.out.println(t.getState());
t.start();
//线程结束后的状态 TERMINATED
Thread.sleep(2000);
System.out.println(t.getState());
}
}
状态转换图
一主线三分支