java thread 源代码_Java读源码之Thread

本文详细剖析了Java Thread类的源代码,包括线程的状态、构造方法、启动过程、中断机制、线程的优先级以及相关的方法如sleep、join等。通过阅读源码,可以深入了解Java线程的工作原理及其在并发编程中的应用。
摘要由CSDN通过智能技术生成

前言

JDK版本:1.8

阅读了Object的源码,wait和notify方法与线程联系紧密,而且多线程已经是必备知识,那保持习惯,就从多线程的源头Thread类开始读起吧。由于该类比较长,只读重要部分

源码

类声明和重要属性

package java.lang;

public class Thread implements Runnable {

private volatile String name;

// 优先级

private int priority;

//是否后台

private boolean daemon = false;

/* JVM state */

private boolean stillborn = false;

// 要跑的任务

private Runnable target;

// 线程组

private ThreadGroup group;

// 上下文加载器

private ClassLoader contextClassLoader;

// 权限控制上下文

private AccessControlContext inheritedAccessControlContext;

// 线程默认名字“Thread-{{ threadInitNumber }}”

private static int threadInitNumber;

// 线程本地局部变量,每个线程拥有各自独立的副本

ThreadLocal.ThreadLocalMap threadLocals = null;

// 有时候线程本地局部变量需要被子线程继承

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

// 线程初始化时申请的JVM栈大小

private long stackSize;

// 线程ID

private long tid;

// 线程init之后的ID

private static long threadSeqNumber;

// 0就是线程还处于NEW状态,没start

private volatile int threadStatus = 0;

// 给LockSupport.park用的需要竞争的对象

volatile Object parkBlocker;

// 给中断用的需要竞争的对象

private volatile Interruptible blocker;

// 线程最小优先级

public final static int MIN_PRIORITY = 1;

// 线程默认优先级

public final static int NORM_PRIORITY = 5;

// 线程最大优先级

public final static int MAX_PRIORITY = 10;

Java线程有几种状态?

// Thread类中的枚举

public enum State {

// 线程刚创建出来还没start

NEW,

// 线程在JVM中运行了,需要去竞争资源,例如CPU

RUNNABLE,

// 线程等待获取对象监视器锁,损被别人拿着就阻塞

BLOCKED,

// 线程进入等待池了,等待觉醒

WAITING,

// 指定了超时时间

TIMED_WAITING,

// 线程终止

TERMINATED;

}

下面这个图可以帮助理解Java线程的生命周期,这个图要会画!面试中被问到,当时画的很不专业,难受!

b2446c2295a83e3d1996b54d89551d03.png

创建

那么线程如何进入初始New状态呢?让我们来看看构造,头皮发麻,怎么有七八个构造,这里只贴了一个

public Thread() {

init(null, null, "Thread-" + nextThreadNum(), 0);

}

还好都是调用init()方法,怕怕的点开了

private void init(ThreadGroup g, Runnable target, String name,

long stackSize, AccessControlContext acc,

boolean inheritThreadLocals) {

if (name == null) {

throw new NullPointerException("name cannot be null");

}

this.name = name;

// 获取当前线程,也就是需要被创建线程的爸爸

Thread parent = currentThread();

SecurityManager security = System.getSecurityManager();

if (g == null) {

// 通过security获取线程组,其实就是拿的当前线程的组

if (security != null) {

g = security.getThreadGroup();

}

// 获取当前线程的组,这下确保肯定有线程组了

if (g == null) {

g = parent.getThreadGroup();

}

}

// check一下组是否存在和是否有线程组修改权限

g.checkAccess();

// 子类执行权限检查,子类不能重写一些不是final的敏感方法

if (security != null) {

if (isCCLOverridden(getClass())) {

security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);

}

}

// 组里未启动的线程数加1,长时间不启动就会被回收

g.addUnstarted();

// 线程的组,是否后台,优先级,初始全和当前线程一样

this.group = g;

this.daemon = parent.isDaemon();

this.priority = parent.getPriority();

if (security == null || isCCLOverridden(parent.getClass()))

// 子类重写check没过或者就没有security,这里要check下是不是连装载的权限都没有

this.contextClassLoader = parent.getContextClassLoader();

else

this.contextClassLoader = parent.contextClassLoader;

// 访问控制上下文初始化

this.inheritedAccessControlContext =

acc != null ? acc : AccessController.getContext();

// 任务初始化

this.target = target;

// 设置权限

setPriority(priority);

// 如果有需要继承的ThreadLocal局部变量就copy一下

if (inheritThreadLocals && parent.inheritableThreadLocals != null)

this.inheritableThreadLocals =

ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

// 初始化JVM中待创建线程的栈大小

this.stackSize = stackSize;

// threadSeqNumber线程号加1

tid = nextThreadID();

}

运行

现在线程已经是NEW状态了,我们还需要调用start方法,让线程进入RUNNABLE状态,真正在JVM中快乐的跑起来,当获得了执行任务所需要的资源后,JVM便会调用target(Runnable)的run方法。

注意:我们永远不要对同一个线程对象执行两次start方法

public synchronized void start() {

// 0就是NEW状态

if (threadStatus != 0)

throw new IllegalThreadStateException();

// 把当前线程加到线程组的线程数组中,然后nthreads线程数加1,nUnstartedThreads没起的线程数减1

group.add(this);

boolean started = false;

// 请求资源

try {

start0();

started = true;

} finally {

try {

if (!started) {

// 起失败啦,把当前线程从线程组的线程数组中删除,然后nthreads减1,nUnstartedThreads加1

group.threadStartFailed(this);

}

} catch (Throwable ignore) {

// start0出问题会自己打印堆栈信息

}

}

}

private native void start0();

终止

现在我们的线程已经到RUNNABLE状态了,一切顺利的话任务执行完成,自动进入TERMINATED状态,天有不测风云,我们还会再各个状态因为异常到达TERMINATED状态。

Thread类为我们提供了interrupt方法,可以设置中断标志位,设置了中断之后不一定有影响,还需要满足一定的条件才能发挥作用:

RUNNABLE状态下

默认什么都不会发生,需要代码中循环检查 中断标志位

WAITING/TIMED_WAITING状态下

这两个状态下,会从对象等待池中出来,等拿到监视器锁会抛出InterruptedException异常,然后中断标志位被清空。

BLOCKED状态下

如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态

NEW/TERMINATE状态下

啥也不发生

// 设置别的线程中断

public void interrupt() {

if (this != Thread.currentThread())

checkAccess();

// 拿一个可中断对象Interruptible的锁

synchronized (blockerLock) {

Interruptible b = blocker;

if (b != null) {

interrupt0(); // 设置中断标志位

b.interrupt(this);

return;

}

}

interrupt0();

}

// 获取当前线程中断标志位,然后重置中断标志位

public static boolean interrupted() {

return currentThread().isInterrupted(true);

}

// 检查线程中断标志位

public boolean isInterrupted() {

return isInterrupted(false);

}

等待

主线已经做完了,下面来看下支线任务,同样重要哦。从线程状态图看到,RUNNABLE状态可以变成BLOCKED,WAITING或TIMED_WAITING。

其中BLOCKED主要是同步方法竞争锁等同步资源造成的,而TIMED_WAITING主要是加了超时时间,其他和WAITING的内容差不多,唯一多了一个sleep方法。

sleep

果不其然,sleep方法和Object.wait方法如出一辙,都是调用本地方法,提供毫秒和纳秒两种级别的控制,唯一区别就是,sleep不会放弃任何占用的监视器锁

public static native void sleep(long millis) throws InterruptedException;

// 纳秒级别控制

public static void sleep(long millis, int nanos) throws InterruptedException {

if (millis < 0) {

throw new IllegalArgumentException("timeout value is negative");

}

if (nanos < 0 || nanos > 999999) {

throw new IllegalArgumentException(

"nanosecond timeout value out of range");

}

if (nanos >= 500000 || (nanos != 0 && millis == 0)) {

millis++;

}

sleep(millis);

}

join

join方法会让线程进入WAITING,等待另一个线程的终止,整个方法和Object.wait方法也是很像,而且实现中也用到了wait,既然用到了wait方法,自然也会释放调用对象的监视器锁

public final synchronized void join(long millis) throws InterruptedException {

long base = System.currentTimeMillis();

long now = 0;

if (millis < 0) {

throw new IllegalArgumentException("timeout value is negative");

}

if (millis == 0) {

// 判断调用join的线程是否活着,这里的活着是指RUNNABLE,BLOCKED,WAITING,TIMED_WAITING这四种状态,如果活着就一直等着,wait(0)意味着无限等

while (isAlive()) {

wait(0);

}

} else {

while (isAlive()) {

long delay = millis - now;

if (delay <= 0) {

break;

}

wait(delay);

now = System.currentTimeMillis() - base;

}

}

}

// 纳秒级别控制

public final synchronized void join(long millis, int nanos)

throws InterruptedException {

if (millis < 0) {

throw new IllegalArgumentException("timeout value is negative");

}

if (nanos < 0 || nanos > 999999) {

throw new IllegalArgumentException(

"nanosecond timeout value out of range");

}

if (nanos >= 500000 || (nanos != 0 && millis == 0)) {

millis++;

}

join(millis);

}

public final void join() throws InterruptedException {

join(0);

}

其他方法

yield

告诉操作系统的调度器:我的cpu可以先让给其他线程,但是我占有的同步资源不让。

注意,调度器可以不理会这个信息。这个方法几乎没用,调试并发bug可能能派上用场

public static native void yield();

setPriority

有些场景是需要根据线程的优先级来调度的,优先级越大越先执行,最大10,默认5,最小1

public final void setPriority(int newPriority) {

ThreadGroup g;

checkAccess();

if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {

throw new IllegalArgumentException();

}

if((g = getThreadGroup()) != null) {

// 如果设置的优先级,比线程所属线程组中优先级的最大值还大,我们需要更新最大值

if (newPriority > g.getMaxPriority()) {

newPriority = g.getMaxPriority();

}

// 本地方法

setPriority0(priority = newPriority);

}

}

弃用方法

有些熟悉的方法已经被弃用了,我们要避免使用

@Deprecated

public final void stop()

@Deprecated

public final synchronized void stop(Throwable obj)

@Deprecated

public void destroy()

@Deprecated

public final void suspend()

@Deprecated

public final void resume()

@Deprecated

public native int countStackFrames()

实践

interrupt()

public class ThreadInterruptTest {

/**

* 如果我们同时调用了notify和interrupt方法,程序有可能正常执行结束,有可能抛出异常结束,

* 原因是不管是因为notify还是interrupt,线程离开了等待池,都需要去竞争锁,

* 如果interrupt调用瞬间拿到锁,notify还没有调用,就抛中断异常

* 如果是interrupt调用瞬间拿不到锁,此时中断标志位被重置,然后notify把线程拉到正常轨道,就继续执行不抛中断异常

*/

private static void testInterrupt() {

Object object = new Object();

Thread thread1 = new Thread(() -> {

synchronized (object) {

try {

object.wait();

System.out.println("我还活着!");

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

e.printStackTrace();

}

}

});

thread1.start();

new Thread(() -> {

// 只为了演示,实际很少用到这些方法,而且我们在执行中断的同步代码块中最好不要做别的事情,例如这里的notify

synchronized (object) {

thread1.interrupt();

object.notify();

}

}).start();

}

public static void main(String[] args) {

for (int i = 0; i <5 ; i++) {

ThreadInterruptTest.testInterrupt();

}

}

}

/**

* 输出:

* 我还活着!

* java.lang.InterruptedException

* at java.lang.Object.wait(Native Method)

* at java.lang.Object.wait(Object.java:502)

* at study.ThreadInterruptTest.lambda$testInterrupt$0(ThreadInterruptTest.java:15)

* at java.lang.Thread.run(Thread.java:748)

* java.lang.InterruptedException

* at java.lang.Object.wait(Native Method)

* at java.lang.Object.wait(Object.java:502)

* at study.ThreadInterruptTest.lambda$testInterrupt$0(ThreadInterruptTest.java:15)

* at java.lang.Thread.run(Thread.java:748)

* 我还活着!

* java.lang.InterruptedException

* at java.lang.Object.wait(Native Method)

* at java.lang.Object.wait(Object.java:502)

* at study.ThreadInterruptTest.lambda$testInterrupt$0(ThreadInterruptTest.java:15)

* at java.lang.Thread.run(Thread.java:748)

*

*/

join()

public class ThreadJoinTest {

public static void main(String[] args) {

Thread thread1 = new Thread(() -> {

System.out.println("你好");

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("你更好!");

});

thread1.start();

new Thread(() -> {

System.out.println("你也好");

try {

thread1.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("你最好!!");

}).start();

}

/**

* 输出:

* 你好

* 你也好

* 你更好!

* 你最好!!

*/

}

以下是使用Java调用摄像头进行拍照的示例代码: ```java import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import com.github.sarxos.webcam.Webcam; public class WebcamCaptureExample extends JPanel implements Runnable { private static final long serialVersionUID = 1L; private Webcam webcam = null; private BufferedImage image = null; public WebcamCaptureExample() { super(); setPreferredSize(new Dimension(640, 480)); webcam = Webcam.getDefault(); webcam.setViewSize(new Dimension(640, 480)); } @Override public void run() { while (true) { try { image = webcam.getImage(); repaint(); Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override protected void paintComponent(java.awt.Graphics g) { super.paintComponent(g); if (image != null) { g.drawImage(image, 0, 0, getWidth(), getHeight(), this); } } public void start() { Thread t = new Thread(this); t.setDaemon(true); t.start(); } public void stop() { webcam.close(); } public static void main(String[] args) throws IOException { WebcamCaptureExample example = new WebcamCaptureExample(); JFrame window = new JFrame("Webcam Capture Example"); window.add(example); window.pack(); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setVisible(true); example.start(); // 保存图片到文件 BufferedImage image = example.image; File file = new File("webcam_capture.png"); ImageIO.write(image, "png", file); example.stop(); } } ``` 该示例程序使用了 [Webcam Capture API](https://github.com/sarxos/webcam-capture) 来获取摄像头图像,需要引入对应的库文件。 程序的主要流程如下: 1. 获取默认摄像头对象并设置摄像头视野大小为640x480。 2. 实现Runnable接口,启动一个线程来获取摄像头图像并通过repaint()方法刷新面板。 3. 实现paintComponent()方法,在面板上绘制摄像头图像。 4. 启动线程,并在主线程保存最后一帧摄像头图像到文件。 5. 在程序结束时关闭摄像头。 注意:在使用该示例代码之前,请先确保电脑上已经安装了摄像头并且能够正常使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值