程序、进程、线程
程序
程序是「静态」的,存放在硬盘中的可执行文件,包括「代码指令」和「数据」
进程
进程是「运行中的程序」,是程序的一次启动和执行:操作系统从磁盘载入程序到内存,分配必要的资源,开始运行程序的指令
进程的组成:「代码段」,「数据段」,「PCB」(id,name,status,priority,物理地址,context,file,others)(描述信息,调度信息,资源信息,上下文)
线程
线程是“进程代码段”的一段「执行流程」
线程的组成:「PC」,「栈内存」(方法的调用和返回对应了栈帧的入栈和出栈),「线程基本信息」(id,name,status,priority,others)
进程和线程的区别
-
一个进程至少有一个线程
-
线程之间共享同一块地址空间(方法去内存,堆内存),打开的文件和其他资源,故上下文切换速度较快
-
调度执行的基本单位:线程
-
资源分配的基本单位:进程
Thread
in Java
Thread
类
public void start()
用来启动一个线程,当调用start()
方法后,JVM才会开启一个新的线程来执行用户定义的线程代码逻辑,在这个过程中会为相应的线程分配需要的资源

public void run()
是线程代码的逻辑入口,不是给用户程序来调用的,当调用start()
方法之后,只要该线程获得了CPU时间片,就会执行run()
方法
创建一个空线程
public class Nov29 {
public static void main(String[] args) {
Thread emptyThread = new Thread("empty thread");
emptyThread.setPriority(5);
System.out.println("emptyThread.getId() = " + emptyThread.getId());
System.out.println("emptyThread.getName() = " + emptyThread.getName());
System.out.println("emptyThread.getPriority() = " + emptyThread.getPriority());
emptyThread.start();
System.out.println("end of emptyThread");
}
}
复制代码
Thread
类中,由于target
对象为null
,run()
方法什么也没做就结束了
@Override
public void run() {
if (target != null) {
target.run();
}
}
复制代码
继承Thread
类
- 继承
Thread
类 - 重写
run()
方法
public class FirstThread extends Thread{
@Override
public void run(){
System.out.println(" run() function in FirstThread ");
}
}
复制代码
public class Nov29 {
public static void main(String[] args) {
Thread firstThread = new FirstThread();
firstThread.start();
}
}
复制代码
实现Runnable
接口
-
定义一个类实现
Runnable
接口 -
实现
run()
方法 -
通过
Thread
类的构造方法public Thread(Runnable target)
传入该类
函数式接口:有且仅有一个方法的接口
public class SecondThread implements Runnable{
@Override
public void run() {
System.out.println(" run() in SecondThread ");
}
}
复制代码
public class Nov29 {
public static void main(String[] args) {
SecondThread secondThread = new SecondThread();
Thread thread = new Thread(secondThread);
thread.start();
}
}
复制代码
对比:
-
通过继承
Thread
类来创建线程,可以直接使用Thread
类的实例的方法 -
实现
Runnable
接口来创建线程则不能直接调用Thread
类的实例的方法,需要通过Thread
类的静态方法public Thread currentThread()
来获取当前类 -
在实际开发中,由于Java不允许多继承,所以一般使用实现
Runnable
接口的方法来创建线程 -
通过实现
Runnable
接口的方法来创建线程能更好地实现数据和逻辑的分离 -
Thread
继承了Runnable
接口,其中的run()
方法无返回值,若需要异步获取程序的执行结构,应该采用实现Callable
的方式
数据和逻辑的分离
todo:这个不太理解,之后需要细看
匿名内部类
public class ThirdThread {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("new a thread using anonymous inner class");
System.out.println("implement method in Runnable");
}
}).start();
}
}
复制代码
class ThirdThread {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
System.out.println("new a thread using anonymous inner class");
System.out.println("override method in Thread");
}
}.start();
}
}
复制代码
Lambda表达式
一行代码搞定
public class Nov29 {
public static void main(String[] args) {
new Thread(() -> System.out.println("new a thread using lambda")).start();
}
}
复制代码
实现Callable
接口
public class FourthThread {
// 1. 新建Callable实现类
static class RunnableTask implements Callable<Long> {
// 2. 重写其中的call()方法,可以有返回值
@Override
public Long call() throws Exception {
Thread.sleep(3000);
return Long.MAX_VALUE;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 3. 使用FutureTask实例作为Thread构造器的target入参,构造新的Thread线程实例。
RunnableTask runnableTask = new RunnableTask();
FutureTask<Long> longFutureTask = new FutureTask<>(runnableTask);
Thread thread = new Thread(longFutureTask);
// 4. 调用Thread实例的start()方法启动新线程,启动新线程的run()方法并发执行
// 其内部的执行过程为:Thread实例的run(),FutureTask实例的run()方法,Callable实现类的call()方法
thread.start();
// 5. 调用FutureTask对象的get()方法阻塞性地获得并发线程的执行结果
System.out.println("longFutureTask.get() = " + longFutureTask.get());
}
}
复制代码
使用线程池
不推荐使用 Executors
创建线程池
public class ThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("无返回值execute()");
}
});
System.out.println("有返回值submit()" + pool.submit(new FourthThread.RunnableTask()).get());
}
}
复制代码
-
execute()
,无返回值,参数只能是Runnable
实现类,不能异步控制只能等其自动停止 -
submit()
,有返回值,参数可以是有返回值的Callable
实现类,可以异步控制
Java线程原理
Java线程和操作系统线程
Java虚拟机把Java线程一对一映射到操作系统线程,线程的调度交给操作系统
在JVM看来,操作系统视角下的 running
状态和 ready
状态统一称为 runnable
状态
优先级
Java线程具有优先级,优先级高的获得调度机会统计规律上大于优先级低的线程,但不绝对
生命周期
public enum State {
NEW,
RUNNABLE, // start()方法调用后,就是runnable状态,但并不一定立刻获得CPU时间片执行run()方法
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED; // 抛出异常或run()方法执行完
}
复制代码
Java线程基本操作
操作
-
sleep:使线程休眠
-
yield:让出CPU时间片,让操作系统重新调度一次
-
interrupt:将线程状态设置为中断,不会像
stop()
方法那样中止一个线程,线程状态设置为中断后线程自己判断是继续执行还是终止 -
join:合并两个线程,让其中一个等待另一个执行完毕再执行;或者设置一个时间,时间到了就不等了
-
daemon:守护线程(为用户进程提供服务的进程,与用户进程的关系是和JVM线程的关系一个是主动一个是被动)
调试
➜ ~ jps
1217 Launcher
1218 ThreadSleep
1220 Jps
807
➜ ~ jstack 1218
2021-11-30 11:20:29
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.301-b09 mixed mode):
"Attach Listener" #12 daemon prio=9 os_prio=31 tid=0x00007fbfcc9fd800 nid=0x4503 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" #11 prio=5 os_prio=31 tid=0x00007fbfcc9fa000 nid=0x1003 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-0" #10 prio=5 os_prio=31 tid=0x00007fbfca8eb800 nid=0x3c03 waiting on condition [0x000070000d21f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at ThreadSleep.lambda$main$0(ThreadSleep.java:5)
at ThreadSleep$$Lambda$1/1828972342.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007fbfca86b800 nid=0x4703 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fbfcc04b000 nid=0x4803 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fbfcc8b5000 nid=0x3703 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fbfcc04a000 nid=0x3503 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fbfca85a000 nid=0x4903 runnable [0x000070000cc0d000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x0000000795713788> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x0000000795713788> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:49)
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fbfcc049000 nid=0x4a03 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fbfca81c800 nid=0x5103 in Object.wait() [0x000070000c901000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000795588ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x0000000795588ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fbfcb01b000 nid=0x5203 in Object.wait() [0x000070000c7fe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000795586c00> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x0000000795586c00> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=31 tid=0x00007fbfca817800 nid=0x2b03 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fbfcc00a000 nid=0x2407 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fbfcc80e800 nid=0x2003 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fbfca80e000 nid=0x2203 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fbfcc80f000 nid=0x2a03 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fbfcc04e000 nid=0x3b03 waiting on condition
JNI global references: 319
复制代码