[TOC] `Thread`类是Java中用于创建和管理线程的核心类。它位于`java.lang`包中,提供了创建、启动、中断和管理线程的方法。 ### 1.Thread源码解读 #### **1.1. 类定义** `Thread`类继承自`Object`类,并实现了`Runnable`接口。此外,它还实现了`Serializable`接口,允许线程对象被序列化。 ```java public class Thread implements Runnable { // ... } ``` #### **1.2. 构造方法** - `Thread()`: 创建一个新的线程。 - `Thread(Runnable target)`: 创建一个新线程并指定要运行的目标(即实现`Runnable`接口的对象)。 - `Thread(Runnable target, String name)`: 创建一个新线程并指定要运行的目标和名称。 - `Thread(String name)`: 创建一个新线程并指定名称。 - `Thread(ThreadGroup group, Runnable target)`: 创建一个新线程并指定线程组和要运行的目标。 - `Thread(ThreadGroup group, Runnable target, String name)`: 创建一个新线程并指定线程组、要运行的目标和名称。 - `Thread(ThreadGroup group, Runnable target, String name, long stackSize)`: 创建一个新线程并指定线程组、要运行的目标、名称和堆栈大小。 #### 1.3. 初始化方法 `init` 方法是所有构造方法调用的共同入口点,负责初始化线程的各种属性。 ```java 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; this.target = target; // 继承父线程的线程组 Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { if (security != null) { g = security.getThreadGroup(); } if (g == null) { g = parent.getThreadGroup(); } } // 检查访问权限 g.checkAccess(); // 检查线程数限制 if (g != null && !g.isDestroyed()) { g.addUnstarted(); } this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(getClass())) { this.contextClassLoader = parent.getContextClassLoader(); } else { this.contextClassLoader = AccessController.doPrivileged( new PrivilegedAction<>() { public ClassLoader run() { return parent.getContextClassLoader(); } }, acc); } this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.stackSize = stackSize; // 设置ID tid = nextThreadID(); } ``` - `name`:线程的名称。 - `target`:要执行的任务(`Runnable`)。 - `group`:线程所属的线程组。 - `daemon`:是否为守护线程,默认继承父线程的守护状态。 - `priority`:线程优先级,默认继承父线程的优先级。 - `contextClassLoader`:线程上下文类加载器。 - `inheritedAccessControlContext`:继承的安全上下文。 - `stackSize`:线程栈大小。 - `tid`:线程的唯一标识符。 #### 1.4. 启动线程 `start` 方法用于启动线程,调用`native`方法`start0`来实际启动线程。 ```java public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); 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 then it will be passed up the call stack */ } } } private native void start0(); ``` - `start0` 是一个`native`方法,由JVM实现,具体细节取决于底层操作系统。 - `group.add(this)` 将新线程添加到线程组中。 - 如果线程已经启动,则抛出`IllegalThreadStateException`异常。 #### 1.5. 线程执行 `run` 方法是线程的实际执行代码。如果通过`Runnable`创建线程,则会调用`Runnable`的`run`方法;否则,直接调用`Thread`的`run`方法。 ```java @Override public void run() { if (target != null) { target.run(); } } ``` #### 1.6. 中断线程 `interrupt` 方法用于中断线程。 ```java public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); } ``` - `interrupt0` 是一个`native`方法,设置线程的中断标志。 - 如果线程当前处于阻塞状态(如等待I/O),则会抛出`InterruptedException`。 #### 1.7. 获取线程状态 `getState` 方法返回线程的状态。 ```java public State getState() { return sun.misc.VM.toThreadState(threadStatus); } ``` - `threadStatus` 是一个内部字段,存储线程的状态信息。 - `toThreadState` 是一个`native`方法,将内部状态转换为公开的`Thread.State`枚举值。 #### 1.8. Thread核心方法总览 - `start()`: 启动线程,使该线程开始执行;JVM调用该线程的`run`方法。 - `run()`: 线程启动后实际执行的方法。如果通过`Runnable`创建线程,则会调用`Runnable`的`run`方法。 - `interrupt()`: 中断线程。如果线程处于阻塞状态(如等待I/O),则会抛出`InterruptedException`。 - `isInterrupted()`: 测试线程是否已经中断。不会清除中断状态。 - `interrupted()`: 测试当前线程是否已经中断,并清除当前线程的中断状态。 - `getName()`: 返回该线程的名称。 - `setName(String name)`: 设置该线程的名称。 - `getId()`: 返回该线程的唯一标识符。 - `getPriority()`: 返回线程的优先级。 - `setPriority(int newPriority)`: 更改线程的优先级。 - `getState()`: 返回线程的状态(如NEW, RUNNABLE, BLOCKED等)。 - `join`:等待线程终止。 - `sleep`:使当前线程暂停执行指定的时间。 - `yield`:提示调度器让出CPU时间片给其他线程。 - `setName` 和 `getName`:设置和获取线程名称。 - `setPriority` 和 `getPriority`:设置和获取线程优先级。 - `isAlive`:检查线程是否存活。 - `isDaemon` 和 `setDaemon`:检查和设置线程是否为守护线程。 - `currentThread`:返回当前正在执行的线程。 - `getThreadLocalMap()`: 获取与当前线程相关的本地变量映射。 - `setContextClassLoader(ClassLoader cl)`: 设置线程上下文类加载器。 - `getContextClassLoader()`: 返回线程上下文类加载器。 - `checkAccess()`: 确保当前线程具有修改此线程的权限。 ### 2.Thread应用示例 下面是一个简单的示例,展示了如何使用`Thread`类的一些核心方法: ```java public class SimpleThreadExample { public static void main(String[] args) { // 创建一个线程 Thread myThread = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("Thread: " + Thread.currentThread().getName() + " - Count: " + i); try { Thread.sleep(1000); // 每隔1秒打印一次 } catch (InterruptedException e) { e.printStackTrace(); } } }, "MyThread"); // 启动线程 myThread.start(); // 主线程等待子线程结束 try { myThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } // 打印主线程信息 System.out.println("Main thread finished"); } } ``` 在这个例子中,我们创建了一个新的线程`myThread`,设置了它的名称,并启动了它。主线程使用`join`方法等待`myThread`结束,然后继续执行并打印一条消息。 ### 3.Thread安全问题 在Java中,多线程编程可以带来性能提升和响应性增强,但同时也引入了一些安全问题。这些安全问题主要与线程间的共享数据访问有关。以下是Java线程中常见的安全问题及其解决方法: #### 3.1. **竞态条件(Race Condition)** - **定义**:当多个线程同时访问并修改同一个共享资源时,最终的结果依赖于线程的执行顺序,这种现象称为竞态条件。 - **示例**:两个线程同时对一个计数器进行递增操作,可能会导致计数器值不正确。 - **解决方法**: - 使用`synchronized`关键字来同步访问共享资源。 - 使用`ReentrantLock`等显式锁。 - 使用原子变量,如`AtomicInteger`。 ```java public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } } ``` [Gitee链接地址,建议收藏,后续我会对专栏进行整理,每篇文章进行校正和调整,然后统一存放在gitee仓库中](https://gitee.com/MyCoder4j/coder4j) #### 3.2. **死锁(Deadlock)** - **定义**:两个或多个线程互相等待对方释放资源,导致所有涉及的线程都无法继续执行。 - **示例**:线程A持有资源X并请求资源Y,而线程B持有资源Y并请求资源X。 - **解决方法**: - 确保所有线程以相同的顺序获取锁。 - 使用超时机制,如`tryLock`方法。 - 避免嵌套锁。 - 使用`Lock`接口提供的工具类,如`ReentrantLock`。 ```java public class DeadlockExample { private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void method1() { synchronized (lock1) { System.out.println("Thread 1: Holding lock 1..."); try { Thread.sleep(10); } catch (InterruptedException e) {} synchronized (lock2) { System.out.println("Thread 1: Holding lock 1 & 2..."); } } } public void method2() { synchronized (lock2) { System.out.println("Thread 2: Holding lock 2..."); try { Thread.sleep(10); } catch (InterruptedException e) {} synchronized (lock1) { System.out.println("Thread 2: Holding lock 1 & 2..."); } } } } ``` #### 3.3. **活锁(Livelock)** - **定义**:两个或多个线程不断重复相同的操作,但没有进展,类似于死锁,但线程并没有被阻塞。 - **示例**:两个线程互相谦让,都试图避免冲突,但结果是它们都无法前进。 - **解决方法**: - 引入随机因素,使线程的行为具有不确定性。 - 使用更复杂的算法来协调线程的行为。 #### 3.4. **内存可见性问题** - **定义**:一个线程对共享变量的修改,另一个线程可能看不到这个修改。 - **示例**:一个线程更新了某个变量,但另一个线程读取到的是旧值。 - **解决方法**: - 使用`volatile`关键字确保变量的修改对所有线程可见。 - 使用`synchronized`关键字,它不仅提供互斥,还保证了内存可见性。 - 使用`Atomic`类,如`AtomicInteger`,它们提供了原子操作和内存可见性。 ```java public class VisibilityExample { private volatile boolean flag = false; public void setFlag() { flag = true; } public boolean getFlag() { return flag; } } ``` #### 3.5. **中断处理不当** - **定义**:线程在执行长时间操作时,如果不能正确响应中断请求,可能会导致资源泄漏或无法及时终止。 - **示例**:线程在等待I/O操作时,如果没有检查中断状态,可能会一直等待下去。 - **解决方法**: - 在长时间操作中定期检查中断状态。 - 捕获`InterruptedException`并恢复中断状态。 ```java public void longRunningTask() throws InterruptedException { while (!Thread.currentThread().isInterrupted()) { try { // 长时间运行的任务 Thread.sleep(1000); } catch (InterruptedException e) { // 恢复中断状态 Thread.currentThread().interrupt(); break; } } } ``` #### 3.6. **线程泄露** - **定义**:线程在完成任务后没有被正确地关闭,导致资源无法释放。 - **示例**:创建大量临时线程,但没有调用`shutdown`方法关闭线程池。 - **解决方法**: - 使用线程池管理线程,并在不需要时调用`shutdown`或`shutdownNow`方法。 - 确保线程在完成任务后能够正常退出。 ```java ExecutorService executor = Executors.newFixedThreadPool(10); // 提交任务 executor.submit(() -> { // 任务代码 }); // 关闭线程池 executor.shutdown(); ``` #### 3.7. **不正确的线程优先级设置** - **定义**:线程优先级设置不当可能导致系统资源分配不合理,影响程序的性能。 - **示例**:将所有线程设置为最高优先级,导致CPU时间片竞争激烈。 - **解决方法**: - 合理设置线程优先级,不要过度依赖优先级。 - 根据实际需求调整线程优先级。 通过理解和应用上述解决方法,可以有效地避免多线程编程中的常见安全问题,确保程序的稳定性和可靠性。 ### 4.Thread类用到的设计模式 `Thread`类是Java中用于创建和管理线程的核心类,它在设计上使用了多种设计模式来提供灵活且强大的功能。以下是`Thread`类中用到的一些主要设计模式: #### 4.1. **模板方法模式 (Template Method Pattern)** - **定义**:模板方法模式定义了一个算法的骨架,而将一些步骤延迟到子类中实现。 - **应用**:`Thread`类中的`run()`方法是一个典型的模板方法。`Thread`类提供了`run()`方法的默认实现,但允许通过继承`Thread`类或实现`Runnable`接口来自定义`run()`方法。 ```java public class Thread implements Runnable { @Override public void run() { if (target != null) { target.run(); } } } ``` 在这个例子中,`run()`方法调用了`target.run()`,如果`target`不为空。这使得用户可以通过实现`Runnable`接口来自定义线程的行为。 #### 4.2. **策略模式 (Strategy Pattern)** - **定义**:策略模式定义了一系列可互换的算法,并将每个算法封装起来,使它们可以互相替换。 - **应用**:`Thread`类可以通过实现`Runnable`接口来传递不同的任务策略。这样,`Thread`对象可以根据传入的不同`Runnable`实例执行不同的任务。 ```java public class MyRunnable implements Runnable { @Override public void run() { // 自定义的任务代码 } } Thread thread = new Thread(new MyRunnable()); thread.start(); ``` 在这个例子中,`MyRunnable`实现了`Runnable`接口,定义了具体的任务策略。`Thread`对象根据这个策略来执行任务。 #### 4.3. **工厂方法模式 (Factory Method Pattern)** - **定义**:工厂方法模式定义了一个创建对象的接口,但让子类决定实例化哪一个类。 - **应用**:虽然`Thread`类本身没有直接使用工厂方法模式,但在创建线程时,可以使用`Executors`工具类提供的工厂方法来创建不同类型的线程池。 ```java ExecutorService executor = Executors.newFixedThreadPool(10); executor.execute(() -> { // 任务代码 }); ``` 在这个例子中,`Executors.newFixedThreadPool(10)`是一个工厂方法,返回一个固定大小的线程池。 #### 4.4. **单例模式 (Singleton Pattern)** - **定义**:单例模式确保一个类只有一个实例,并提供一个全局访问点。 - **应用**:虽然`Thread`类本身不是单例,但在某些情况下,如守护线程(如垃圾回收线程),可能会使用单例模式来确保系统中只有一个实例。 #### 4.5. **适配器模式 (Adapter Pattern)** - **定义**:适配器模式将一个类的接口转换成客户端所期望的另一个接口。 - **应用**:`Thread`类通过实现`Runnable`接口并调用`Runnable`的`run()`方法,实际上充当了一个适配器的角色,将`Runnable`接口适配为`Thread`类的一部分。 ```java public class Thread implements Runnable { private Runnable target; public Thread(Runnable target) { this.target = target; } @Override public void run() { if (target != null) { target.run(); } } } ``` 在这个例子中,`Thread`类通过构造函数接收一个`Runnable`对象,并在其`run()`方法中调用`Runnable`的`run()`方法,从而将`Runnable`适配为`Thread`的一部分。 #### 4.6. **观察者模式 (Observer Pattern)** - **定义**:观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 - **应用**:虽然`Thread`类本身没有直接使用观察者模式,但在多线程编程中,可以使用`wait()`和`notify()`/`notifyAll()`方法来实现类似的功能。一个线程等待某个条件满足时,可以调用`wait()`方法进入等待状态,当条件满足时,另一个线程可以调用`notify()`或`notifyAll()`方法来唤醒等待的线程。 ```java synchronized (lock) { while (!condition) { lock.wait(); // 等待条件满足 } // 条件满足后执行的操作 } // 另一个线程 synchronized (lock) { condition = true; // 改变条件 lock.notifyAll(); // 通知所有等待的线程 } ``` 在这个例子中,`wait()`和`notify()`/`notifyAll()`方法类似于观察者模式中的订阅和通知机制。 [Gitee链接地址,建议收藏,后续我会对专栏进行整理,每篇文章进行校正和调整,然后统一存放在gitee仓库中](https://gitee.com/MyCoder4j/coder4j)