线程
线程是什么?
线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。
线程的状态
Java语言定义了5种线程状态,在任意一个时间点,一个线程只能有且只有其中一个状态。这5种状态如下:
- 新建(
New
):创建后尚未启动的线程处于这种状态 - 运行(
Runable
):Runable
包括了操作系统线程状态的Running
和Ready
,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。 - 等待(
Wating
):处于这种状态的线程不会被分配CPU执行时间。等待状态又分为无限期等待和有限期等待,处于无限期等待的线程需要被其他线程显示地唤醒,没有设置Timeout
参数的Object.wait()
、没有设置Timeout
参数的Thread.join()
方法都会使线程进入无限期等待状态;有限期等待状态无须等待被其他线程显示地唤醒,在一定时间之后它们会由系统自动唤醒,Thread.sleep()
、设置了Timeout
参数的Object.wait()
、设置了Timeout
参数的Thread.join()
方法都会使线程进入有限期等待状态。 - 阻塞(
Blocked
):线程被阻塞了,“阻塞状态”与”等待状态“的区别是:”阻塞状态“在等待着获取到一个排他锁,这个时间将在另外一个线程放弃这个锁的时候发生;而”等待状态“则是在等待一段时间或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态。 - 结束(
Terminated
):已终止线程的线程状态,线程已经结束执行。
创建单线程的方式
- 继承
Thread
类
public class ThreadTest {
public static void main(String[] args) {
//设置线程名字
Thread.currentThread().setName("main thread");
MyThread myThread = new MyThread();
myThread.setName("子线程:");
//开启线程
myThread.start();
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
class MyThread extends Thread{
//重写run()方法
public void run(){
for(int i = 0;i < 10; i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
- 实现
Runnable
接口
public class RunnableTest {
public static void main(String[] args) {
//设置线程名字
Thread.currentThread().setName("main thread:");
Thread thread = new Thread(new MyRunnable());
thread.setName("子线程:");
//开启线程
thread.start();
for(int i = 0; i <5;i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
- 实现
Callable
接口。相较于实现Runnable
接口的实现,方法可以有返回值,并且抛出异常。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//实现Callable接口
public class CallableTest {
public static void main(String[] args) {
//执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
new Thread(futureTask).start();
//接收线程运算后的结果
try {
Integer sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
什么是FutureTask?
FutureTask
可⽤于异步获取执⾏结果或取消执⾏任务的场景。通过传⼊Runnable
或者Callable
的任务给FutureTask
,直接调⽤其run
⽅法或者放⼊线程池执⾏,之后可以在外部通过FutureTask
的get
⽅法异步获取执⾏结果,因此,FutureTask
⾮常适合⽤于耗时的计算,主线程可以在完成⾃⼰的任务后,再去获取结果。另外,FutureTask
还可以确保即使调⽤了多次run
⽅法,它都只会执⾏⼀次Runnable
或者Callable
任务,或者通过cancel
取消FutureTask
的执⾏等。futuretask
可⽤于执⾏多任务、以及避免⾼并发情况下多次创建数据机锁的出现。
Runnable接口 和 Callable接口有什么区别?
-
Runnable
接⼝中的run()
⽅法的返回值是void
,它做的事情只是纯粹地去执⾏run()
⽅法中的代码⽽已; -
Callable
接⼝中的call()
⽅法是有返回值的,是⼀个泛型,和Future、FutureTask
配合可以⽤来获取异步执⾏的结果。
start()⽅法和run()⽅法的区别?
start()
⽅法来启动⼀个线程,真正实现了多线程运⾏。- 如果直接调⽤
run()
,其实就相当于是调⽤了⼀个普通函数⽽已,直接调⽤run()
⽅法必须等待run()
⽅法执⾏完毕才能执⾏下⾯的代码,所以执⾏路径还是只有⼀条,根本就没有线程的特征,所以在多线程执⾏时要使⽤start()
⽅法⽽不是run()
⽅法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通