:::tips
作者:LemonCat🐱
时间:2023/8/1
:::
膜拜大神
Java 并发相关的包
java.util.concurrentjava.util.concurrent.atomicjava.util.concurrent.locks

Doug Lea
百度百科:百度百科-Doug Lea
java.util.concurrent包的作者 -> 并发包创始人!!! 跪拜!!!
为什么要学习多线程
硬件方面
- 摩尔定律失效
- 单核性能翻倍 -> 遇到了瓶颈
- 单核主频提高有限 -> 倡导多核处理器
- Java 多线程更好利用CPU资源 -> 想要程序运行更快更好 - 需要我们的并行和3并发编程
软件方面
- 可以用来吹牛X o.O
- 充分利用多核处理器
- 提高程序性能,高并发系统
- 提高程序吞吐量,异步+回调等生产需求
弊端及问题
- 线程安全问题 -> 比如熟知的买票问题
- 线程锁问题
- 线程性能问题
简单的 start
下载 OpenJDK8 源代码
OpenJDK OpenJDK
为什么需要下载 OpenJDK
- 进入官网 下载

- 找到 Java8 SE -> 点击下载

- 解压完成

start 线程解读
- 我们看看究竟是怎样启动(start)一个线程的
调用步骤:
线程初始Java程序
public static void main(String[] args) {Thread t1 = new Thread(() -> {}, "t1");t1.start();}
进入
start方法(表示线程开始的方法)- 我们发现
start方法中 真正开启线程的方法是->start0 调用完
start0方法我们看到started = true;表示线程已经开始 ```java public synchronized void start() { /**- This method is not invoked for the main method thread or “system”
- group threads created/set up by the VM. Any new functionality added
- to this method in the future may have to also be added to the VM. *
- A zero status value corresponds to state “NEW”. */ if (threadStatus != 0) throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
- so that it can be added to the group’s list of threads
- and the group’s unstarted count can be decremented. */ group.add(this);
boolean started = false; try { start0(); started = true; } finally { try {
if (!started) {group.threadStartFailed(this);}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */
} } }
- 我们发现
3. 进入 `start0` -> 我们发现这是个 native 方法 我们进不去了 底层是 C/C++```javaprivate native void start0();
- native 是调用了本地方法, 找到 OpenJDK 查看其源码 ->
../openjdk-8\hotspot

- 这里 java -version 显示的版本 中 Java HotSpot
- 就是右边的对应的 hotspot
- 就是 C/C++对 Java 底层方法的描绘

- 接下来 6 7 8 我们详细的研究一下
private native void start0();-> 底层究竟做了什么! Thread.c-> 路径:..\openjdk-8\jdk\src\share\native\java\lang- Java 线程是通过
start的方法启动执行的,主要内容在native方法start0中 - OpenJDK 的写 JNI 一般是一一对应的 即
Thread.java对应的就是Thread.c - start0 其实就是JVM_StartThread。此时查看源代码可以看到在jvm.h中找到了声明,jvm.cpp中有实现
- Java 线程是通过

- 我们可以看到
start0方法没有返回值 调用了JVM_StartThread即调用 JVM 去开启一个线程jvm.cpp-> 路径·:..\openjdk-8\hotspot\src\share\vm\prims
Thread::start(native_thread);-> 操作系统给我们提供了 一个线程


Thread.cpp- 我们有 Thread.java -> 就会有 Thread.cpp
- 终于在这里调用了 操作系统的线程启动
os::start_thread(thread); - 通过注释我们能知道 -> 操作系统提供的原生线程!帮助我们完成线程的启动~
Java 多线程相关概念
一把锁
-
两个并
-
并发(concurent)
是在同一实体上的多个事件,是在同一台处理器上“同时”处理多个任务,同一时刻,其实是只有一个事件在发生。
并行(parallel)
是在不同实体上的多个事件,是在多台处理器上同时处理多个任务,同一时刻,大家都真的在做事情,你做你的,我做我的
并发 VS 并行
Concurrent -> 两队抢一个咖啡机
- Parallel -> 两队各自有一台咖啡机
三个程
简而言之:管程就是一个对象对应的那个 Monitor 监视器 对象
参考资料:JVM 第三版
理解:
- o 这个对象加了 synchronized 这个锁 -> 就变成了一个 Monitor 监视器
- 哪个线程抢到了这把锁o 才能执行(即现在 t2 有了这把锁 其他线程只能等待)
- 可以类比成 -> 排队上厕所 人就是线程 o 就是厕所门的锁 ```java Object o = new Object();
new Thread(() -> { synchronized (o) { // 监视器 o(一把锁)
}
}, “t2”).start();
<a name="kfVRx"></a>### 用户线程和守护线程Java 线程分为用户线程(User Thread) 和 守护线程(Daemon Thread)- 不做过多配置 - 默认是用户线程<a name="lBi8W"></a>#### 用户线程是系统的工作线程,它会完成这个程序需要完成的业务操作<a name="Ezi4H"></a>#### 守护线程是一种特殊的线程,为其他线程服务的,在后台默默地完成一些系统性的服务 比如 垃圾回收线程。<a name="UaQJO"></a>#### 示例 1- main 线程 -> 主线程 用户线程- 垃圾回收线程 -> 隐藏的 守护线程```javapublic class ThreadStart {public static void main(String[] args) { // Java 一切方法的入口}}
Daemon 属性
线程的 daemon 属性决定了是哪种线程
Thread 类 源码中有两个方法可以设置线程属性
setDaemon()-> 设置守护线程isDaemon()-> 判断是否为守护线程true表示是 守护线程false表示是 用户线程
示例 2
- 两个线程 分别是 main 线程 和 t1 线程
Code:
public class ThreadStart02 {// main 线程public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName() + ":线程开始工作---" +(Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));// t1 线程Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ":线程开始工作---" +(Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));while (1 == 1) {}}, "t1");t1.start();Thread.sleep(3000); // main 线程休息 3sSystem.out.println(Thread.currentThread().getName() + ":线程结束工作");}}
Show:
- 两者默认都是 用户线程 - 两者互不打扰
main 线程即便结束 程序不会结束 因为有 t1 这个用户线程 仍在工作
示例 3
在示例 2 的基础上 将 t1 设置成守护线程
t1.setDaemon(true);
Code:
public class ThreadStart02 {// main 线程public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName() + ":线程开始工作---" +(Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));// t1 线程Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ":线程开始工作---" +(Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));while (1 == 1) {}}, "t1");t1.setDaemon(true);t1.start();Thread.sleep(3000); // main 线程休息 3sSystem.out.println(Thread.currentThread().getName() + ":线程结束工作");}}
Show:
- t1 是守护线程,当main线程结束是,作为一个服务线程,没有服务对象就没有必要继续运行了
- 如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统可退出了。
- 假如当系统只剩下守护线程的时候,java虚拟机会 自动退出
-
示例 4
我们将示例 3 中设置守护线程的代码放到开始后
t1.start();t1.setDaemon(true); // 守护线程
Code:
public class ThreadStart02 {// main 线程public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName() + ":线程开始工作---" +(Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));// t1 线程Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ":线程开始工作---" +(Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));while (1 == 1) {}}, "t1");t1.start();t1.setDaemon(true); // 守护线程Thread.sleep(3000); // main 线程休息 3sSystem.out.println(Thread.currentThread().getName() + ":线程结束工作");}}
Show:
setDaemon(true)方法必须在start()之前设置,否则报IIIegalThreadStateException异常

- 在
setDaemon()的源码中我们也能知道 这个方法必须放在 线程开始之前

