предисловие
Java
Поток в используетThread
класс реализован,Thread
для начинающихJava
Я изучил его в молодости и использовал на практике, но я никогда не видел его реализации с точки зрения исходного кода, сегодня я снова изучу его с точки зрения исходного кода.Java Thread
, может позжеThread
практика удобнее.
Начните с заметки
кажется, я читалJDK
Изучающие исходный код могут почувствоватьJDK
В исходном коде есть очень подробные комментарии. При чтении исходного кода определенного класса вы должны сначала посмотреть введение комментария. Исходный текст комментария не будет опубликован. Ниже приводится мое резюме:
-
Thread
поток выполнения в программе,Java
Виртуальные машины позволяют приложениям разрешить несколько потоков выполнения одновременно. -
Каждый поток имеет понятие приоритета, поток с более высоким приоритетом выполняется в приоритете перед потоком с более низким приоритетом.
-
Каждый поток может быть установлен как поток демона
-
Когда код, работающий в потоке, создает новый
Thread
объект, приоритет нового потока такой же, как у потока создания -
когда
Java
Когда виртуальная машина запустится, она запуститmain
поток, у него нет потока демона,main
Поток будет продолжать выполняться до тех пор, пока не будут отправлены следующие условия-
Runtime
метод выхода из классаexit
вызывается, и менеджер безопасности разрешает операцию выхода - все потоки, не являющиеся демонами, умерли, или
run
Выполнение метода завершается и возвращает результат в обычном режиме, илиrun
метод выдает исключение
-
-
Первый способ создания потока: наследование
Thread
класс, переопределитьrun
метод//定义线程类 class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } } //启动线程 PrimeThread p = new PrimeThread(143); p.start();
-
Второй способ создания потока: реализация
Runnable
интерфейс, переопределениеrun
метод, потому чтоJava
Ограничение единственного наследования//定义线程 class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } } //启动线程 PrimeRun p = new PrimeRun(143); new Thread(p).start();
-
При создании треда вы можете указать имя для треда, если не указать, то для него будет автоматически сгенерировано имя
-
Если не указано иное,
null
параметр, переданныйThread
Конструктор или метод в классе вызовет бросокNullPointerException
Общие свойства потока
прочитать одинJava
class, начнем с того, какими свойствами он обладает:
//线程名称,创建线程时可以指定线程的名称
private volatile String name;
//线程优先级,可以设置线程的优先级
private int priority;
//可以配置线程是否为守护线程,默认为false
private boolean daemon = false;
//最终执行线程任务的`Runnable`
private Runnable target;
//描述线程组的类
private ThreadGroup group;
//此线程的上下文ClassLoader
private ClassLoader contextClassLoader;
//所有初始化线程的数目,用于自动编号匿名线程,当没有指定线程名称时,会自动为其编号
private static int threadInitNumber;
//此线程请求的堆栈大小,如果创建者没有指定堆栈大小,则为0。, 虚拟机可以用这个数字做任何喜欢的事情。, 一些虚拟机会忽略它。
private long stackSize;
//线程id
private long tid;
//用于生成线程ID
private static long threadSeqNumber;
//线程状态
private volatile int threadStatus = 0;
//线程可以拥有的最低优先级
public final static int MIN_PRIORITY = 1;
//分配给线程的默认优先级。
public final static int NORM_PRIORITY = 5;
//线程可以拥有的最大优先级
public final static int MAX_PRIORITY = 10;
Все имена атрибутов очень семантичны. На самом деле, вы можете догадаться, что они делают, увидев название. Это не так уж сложно~~
Конструктор потоков
Разобравшись со свойствами, посмотрите наThread
Как строятся экземпляры? Сначала просмотрите, сколько у него методов строительства:
Глядя на внутренний исходный код каждого метода построения, обнаруживается, что имя называетсяinit
частный метод, см. еще разinit
Метод имеет две перегрузки, и его основной метод выглядит следующим образом:
/**
* Initializes a Thread.
*
* @param g 线程组
* @param target 最终执行任务的 `run()` 方法的对象
* @param name 新线程的名称
* @param stackSize 新线程所需的堆栈大小,或者 0 表示要忽略此参数
* @param acc 要继承的AccessControlContext,如果为null,则为 AccessController.getContext()
* @param inheritThreadLocals 如果为 true,从构造线程继承可继承的线程局部的初始值
*/
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) {
//如果安全管理器不为空
if (security != null) {
//获取SecurityManager中的线程组
g = security.getThreadGroup();
}
//如果获取的线程组还是为空
if (g == null) {
//则使用父线程的线程组
g = parent.getThreadGroup();
}
}
//检查安全权限
g.checkAccess();
//使用安全管理器检查是否有权限
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()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
//初始化当前线程对象的最终执行任务对象
this.target = target;
//这里再对线程的优先级字段进行处理
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
//初始化当前线程对象的堆栈大小
this.stackSize = stackSize;
//初始化当前线程对象的线程ID,该方法是同步的,内部实际上是threadSeqNumber++
tid = nextThreadID();
}
еще одна перегрузкаinit
Приватный метод выглядит следующим образом, на самом деле внутренний вызов вышеinit
метод:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
Затем посмотрите на все конструкторы:
-
пустой конструктор
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
Внутренний вызов
init
Второй перегруженный метод, параметры в основном являются значениями по умолчанию, имя потока жестко закодировано как"Thread-" + nextThreadNum()
Формат,nextThreadNum()
Для синхронизированного метода поддерживается внутреннее статическое свойство, указывающее количество потоков инициализации + 1:private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
-
Пользовательские задачи выполнения
Runnable
конструктор объектаpublic Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
Отличие от первого конструктора в том, что его можно настраивать
Runnable
объект -
Пользовательские задачи выполнения
Runnable
объект иAccessControlContext
конструктор объектовThread(Runnable target, AccessControlContext acc) { init(null, target, "Thread-" + nextThreadNum(), 0, acc, false); }
-
пользовательская группа потоков
ThreadGroup
и выполнять заданияRunnable
конструктор объектовpublic Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); }
-
пользовательское имя потока
name
метод строительстваpublic Thread(String name) { init(null, null, name, 0); }
-
пользовательская группа потоков
ThreadGroup
и название темыname
метод строительстваpublic Thread(ThreadGroup group, String name) { init(group, null, name, 0); }
-
Пользовательские задачи выполнения
Runnable
Имена объектов и потоковname
метод строительстваpublic Thread(Runnable target, String name) { init(null, target, name, 0); }
-
пользовательская группа потоков
ThreadGroup
и название темыname
и выполнять заданияRunnable
конструктор объектовpublic Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); }
-
Все свойства являются пользовательскими конструкторами
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); }
Thread
Предоставляет очень гибкий перегруженный метод построения, который разработчикам удобно настраивать под различные параметры.Thread
объект.
Общий метод
Вот некоторые из наиболее распространенных методов дляThread
Есть некоторые локальные методы в , давайте оставим их в покое~
установить имя потока
Задайте имя потока.Этот метод является методом синхронизации.Во избежание проблем с безопасностью потока вы можете вызвать его вручную.Thread
Метод экземпляра задает имя также в конструктореThread
При передаче имени потока в конструктор мы обычно задаем параметры при построении
public final synchronized void setName(String name) {
  //检查安全权限
checkAccess();
  //如果形参为空,抛出空指针异常
if (name == null) {
throw new NullPointerException("name cannot be null");
}
//给当前线程对象设置名称
this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}
получить имя потока
Внутренне напрямую возвращает свойство имени текущего объекта потока
public final String getName() {
return name;
}
начать нить
public synchronized void start() {
//如果不是刚创建的线程,抛出异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
//通知线程组,当前线程即将启动,线程组当前启动线程数+1,未启动线程数-1
group.add(this);
//启动标识
boolean started = false;
try {
//直接调用本地方法启动线程
start0();
//设置启动标识为启动成功
started = true;
} finally {
try {
//如果启动呢失败
if (!started) {
//线程组内部移除当前启动的线程数量-1,同时启动失败的线程数量+1
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
Наш обычный поток запуска должен вызыватьThread
изstart()
метод, тоJava
Виртуальная машина позвонитThred
изrun
метод, вы можете увидетьThread
класс также реализуетRunnable
интерфейс, переписанrun
метод:
@Override
public void run() {
//当前执行任务的Runnable对象不为空,则调用其run方法
if (target != null) {
target.run();
}
}
Thread
два способа использования:
- наследовать
Thread
класс, переопределитьrun
метод, то он непосредственно выполняется в это времяrun
Логика метода, не буду использоватьtarget.run();
- выполнить
Runnable
интерфейс, переопределениеrun
метод, потому чтоJava
Ограничение единственного наследованияRunnable
реализовать
Настроить поток демона
Важнейшей операцией является установкаdaemon
Атрибуты
public final void setDaemon(boolean on) {
//检查是否有安全权限
checkAccess();
//本地方法,测试此线程是否存活。, 如果一个线程已经启动并且尚未死亡,则该线程处于活动状态
if (isAlive()) {
//如果线程先启动后再设置守护线程,将抛出异常
throw new IllegalThreadStateException();
}
//设置当前守护线程属性
daemon = on;
}
Определить, является ли поток потоком демона
public final boolean isDaemon() {
//直接返回当前对象的守护线程属性
return daemon;
}
состояние потока
Сначала идет диаграмма состояний потока:
Получить статус потока:
public State getState() {
//由虚拟机实现,获取当前线程的状态
return sun.misc.VM.toThreadState(threadStatus);
}
Состояние потока в основном определяется классом внутреннего перечисления.State
сочинение:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
- NEW: только что созданный и еще не запущенный поток находится в этом состоянии.
- RUNNABLE: поток, выполняющийся на виртуальной машине Java, находится в этом состоянии.
- BLOCKED: поток, заблокированный в ожидании блокировки монитора, находится в этом состоянии, например, поток, столкнувшийся с
synchronized
Синхронизированный блок, он войдет в это состояние, после чего поток приостановит выполнение до тех пор, пока не будет получена запрошенная блокировка. - ОЖИДАНИЕ: в этом состоянии находится поток, бесконечно ожидающий выполнения другим потоком определенного действия.
- Поток, ожидающий через метод wait(), ожидает метод notify()
- Потоки, ожидающие выполнения метода join(), будут ожидать завершения целевого потока.
- TIMED_WAITING: Ожидание выполнения действия другим потоком до тех пор, пока поток с указанным временем ожидания не окажется в этом состоянии.
- Через метод wait() с тайм-аутом ожидающий поток ожидает метод notify()
- Через метод join() переносится период ожидания, и ожидающий поток будет ждать завершения целевого потока.
- TERMINATED: завершенный поток находится в этом состоянии, и в этот момент поток не может вернуться в состояние RUNNABLE.
нить сна
Это статический нативный метод, который приостанавливает текущий исполняемый поток и приостанавливает выполнение.millis
миллисекунды, выбрасываемые при прерывании снаInterruptedException
исключение прерывания
/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
*
* @param millis
* the length of time to sleep in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public static native void sleep(long millis) throws InterruptedException;
Проверить жив ли поток
Собственный метод, который проверяет, жив ли этот поток. Поток жив, если он был запущен и не умер.
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
*
* @return <code>true</code> if this thread is alive;
* <code>false</code> otherwise.
*/
public final native boolean isAlive();
приоритет потока
- Установить приоритет потока
/**
* Changes the priority of this thread.
* <p>
* First the <code>checkAccess</code> method of this thread is called
* with no arguments. This may result in throwing a
* <code>SecurityException</code>.
* <p>
* Otherwise, the priority of this thread is set to the smaller of
* the specified <code>newPriority</code> and the maximum permitted
* priority of the thread's thread group.
*
* @param newPriority priority to set this thread to
* @exception IllegalArgumentException If the priority is not in the
* range <code>MIN_PRIORITY</code> to
* <code>MAX_PRIORITY</code>.
* @exception SecurityException if the current thread cannot modify
* this thread.
* @see #getPriority
* @see #checkAccess()
* @see #getThreadGroup()
* @see #MAX_PRIORITY
* @see #MIN_PRIORITY
* @see ThreadGroup#getMaxPriority()
*/
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);
}
}
прерывание потока
есть одинstop()
Экземплярный метод может принудительно завершать поток, но этот метод был помечен как устаревший, потому что он слишком агрессивен, и программистам не рекомендуется его использовать, потому чтопринудительно завершить потокприведет к несогласованности данных.
Здесь есть три метода прерывания потока:
//实例方法,通知线程中断,设置标志位
public void interrupt(){}
//静态方法,检查当前线程的中断状态,同时会清除当前线程的中断标志位状态
public static boolean interrupted(){}
//实例方法,检查当前线程是否被中断,其实是检查中断标志位
public boolean isInterrupted(){}
Анализ метода Interrupt()
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
//检查是否是自身调用
if (this != Thread.currentThread())
//检查安全权限,这可能导致抛出{@link * SecurityException}。
checkAccess();
//同步代码块
synchronized (blockerLock) {
Interruptible b = blocker;
//检查是否是阻塞线程调用
if (b != null) {
//设置线程中断标志位
interrupt0();
//此时抛出异常,将中断标志位设置为false,此时我们正常会捕获该异常,重新设置中断标志位
b.interrupt(this);
return;
}
}
//如无意外,则正常设置中断标志位
interrupt0();
}
- Метод прерывания потока не приводит к немедленному завершению потока, а отправляет уведомление в поток, чтобы сообщить целевому потоку, что кто-то хочет, чтобы вы вышли~
- Может вызываться только сам по себе, иначе может выкинуть
SecurityException
- Вызов метода прерывания определяется самим целевым потоком, прерывать ли его, и вызывается ли он одновременно
wait
,join
,sleep
и другие методы, текущий поток перейдет в состояние блокировки, что может произойти в это время.InterruptedException
аномальный - Неразумно, чтобы заблокированный поток снова вызывал метод прерывания.
- Прерывание неактивного потока не имеет никакого эффекта
Проверьте, не прерван ли поток:
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
测试此线程是否已被中断。, 线程的<i>中断*状态</ i>不受此方法的影响。
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if this thread has been interrupted;
* <code>false</code> otherwise.
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}
Статический метод очистит бит флага прерывания текущего потока:
/**
*测试当前线程是否已被中断。, 此方法清除线程的* <i>中断状态</ i>。, 换句话说,如果要连续两次调用此方法,则* second调用将返回false(除非当前线程再次被中断,在第一次调用已清除其中断的*状态 之后且在第二次调用已检查之前), 它)
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if the current thread has been interrupted;
* <code>false</code> otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
Суммировать
запишите, как вы читаетеThread
Некоторые мысли по исходному коду класса, но я могу только держаться подальше от многих локальных методов, используемых в нем, и есть еще некоторые коды, которые я не понимаю. Позже на практикеThread
Делайте больше сводных записей.
наконец
Из-за длины статьи давайте пока запишем их. Оригинальные статьи будут время от времени обновляться в будущем. Добро пожаловать в публичный аккаунт «Чжан Шаолинь»!