Java核心知识体系1:泛型机制详解
Java核心知识体系2:注解机制详解
Java核心知识体系3:异常机制详解
Java核心知识体系4:AOP原理和切面应用
Java核心知识体系5:反射机制详解
Java核心知识体系6:集合框架详解
Java核心知识体系7:线程不安全分析
Java核心知识体系8:Java如何保证线程安全性
Java线程基础主要包含如下知识点,相信我们再面试的过程中,经常会遇到类似的提问。
下面我们 一 一 解读。
如上图,创建完线程,但尚未启动。
如上图,处于可运行阶段,正在运行,或者正在等待 CPU 时间片。包含了 Running
和 Ready
两种线程状态。
如上图,正被Lock住,等待获取一个排它锁,如果其他的线程释放了锁,该状态就会结束。
如上图,处在无限期等待阶段,等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。
主要有两种方式进行释放:
如上图,因为有时间控制,所以无需等待其它线程显式地唤醒,一定时间之后,系统会自动唤醒。
所以他有三种方式进行释放:
主要有两种方式进行释放:
在Java中,线程的实现方式主要有两种:继承Thread
类和实现Runnable
接口。此外,Java 5开始,引入了java.util.concurrent
包,提供了更多的并发工具,如Callable
接口与Future
接口,它们主要用于任务执行。
通过继承Thread
类来创建线程是最基本的方式。你需要创建一个扩展自Thread
类的子类,并重写其run()
方法。然后,可以创建该子类的实例来创建新的线程。
class MyThread extends Thread {
public void run() {
System.out.println("线程运行中");
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 调用start()方法来启动线程
}
}
另一种方式是让你的类实现Runnable
接口,并实现run()
方法。然后,你可以创建Thread
类的实例,将实现了Runnable
接口的类的实例作为构造参数传递给它。
class MyRunnable implements Runnable {
public void run() {
System.out.println("线程运行中");
}
}
public class RunnableDemo {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start(); // 调用start()方法来启动线程
}
}
虽然Callable
和Future
不是直接用于创建线程的,但它们提供了一种更灵活的方式来处理线程执行的结果。Callable
类似于Runnable
,但它可以返回一个结果,并且可以抛出异常。Future
用于获取Callable
执行的结果。
import java.util.concurrent.*;
class MyCallable implements Callable<String> {
public String call() throws Exception {
return "任务完成";
}
}
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
System.out.println(future.get()); // 阻塞等待获取结果
executor.shutdown();
}
}
ExecutorService
等高级并发工具一起使用。Java 中的线程管理机制非常强大,涵盖了从简单的线程创建到复杂的线程池管理等多个方面。
Executor
框架是 Java 并发包(java.util.concurrent
)中的一个关键组件,它提供了一种更高级别的抽象来管理线程池。通过使用 Executor
,你可以更容易地控制线程的创建、执行、调度、生命周期等。它主要有三种类型:
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}
executor.shutdown();
守护线程是一种特殊的线程,它主要用于程序中“后台”任务的支持。守护线程与普通线程的区别在于,当程序中所有非守护线程结束时,JVM 会自动退出,即使还有守护线程在运行。守护线程常用于垃圾回收、JVM 内部的监控等任务。
设置守护线程:通过调用线程对象的 setDaemon(true)
方法,在启动线程之前将其设置为守护线程。
Thread thread = new Thread(new MyRunnable());
thread.setDaemon(true);
sleep()
方法是 Thread
类的一个静态方法,用于让当前正在执行的线程暂停执行指定的时间(毫秒),以毫秒为单位。在指定的时间过去后,线程将回到可运行状态,等待CPU的调度。
sleep()
方法不会释放锁(如果当前线程持有锁的话)。 try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
yield()
方法也是 Thread
类的一个静态方法,它告诉调度器当前线程愿意放弃当前处理器的使用,但这并不意味着线程会立即停止执行或进入等待/阻塞状态。
调度器可以忽略这个提示,继续让当前线程运行。
yield()
方法不会使线程进入阻塞状态,也不会释放锁(如果持有的话),类似仅建议。Thread.yield();
在Java中,线程中断是一种重要的线程间通信机制,用于通知线程应该停止当前正在执行的任务。线程中断的方式主要有以下几种:
interrupt()
方法interrupt()
方法是Java推荐的线程中断方式。它并不会直接停止线程,而是设置线程的中断状态为true。线程需要定期检查这个中断状态(通过isInterrupted()
方法),并根据需要自行决定如何响应中断请求,比如退出循环、释放资源等。
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务
}
// 线程中断后的清理工作
});
thread.start();
// 稍后中断线程
thread.interrupt();
Executor
的中断操作Java中的线程互斥同步是并发编程中的一个重要概念,用于保证多个线程在访问共享资源时的互斥性,即同一时间只有一个线程能够访问某个资源。Java提供了多种机制来实现线程的互斥同步,主要包括以下几种方式:
1. 基本概念:synchronized是Java中最基本的同步机制,它可以用来修饰方法或代码块。当一个线程访问一个被synchronized修饰的方法或代码块时,其他试图访问该方法或代码块的线程将被阻塞,直到当前线程执行完毕释放锁。
2. 使用方法:
public synchronized void method() {...}
。synchronized(this) {...}
或synchronized(某个对象) {...}
。3. 特性:
4. 示例:
public class Counter {
private int count = 0;
// synchronized修饰方法
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class TestSynchronized {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + counter.getCount());
}
}
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {...} finally { lock.unlock(); }
。tryLock()
等方法,尝试获取锁,如果获取不到则不会阻塞线程。import java.util.concurrent.locks.ReentrantLock;
public class CounterWithLock {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock(); // 创建ReentrantLock对象
public void increment() {
lock.lock(); // 加锁
try {
count++;
} finally {
lock.unlock(); // 释放锁,放在finally块中确保一定会被释放
}
}
public int getCount() {
lock.lock(); // 加锁
try {
return count;
} finally {
lock.unlock(); // 释放锁
}
}
}
public class TestReentrantLock {
public static void main(String[] args) throws InterruptedException {
CounterWithLock counter = new CounterWithLock();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + counter.getCount());
}
}
对于大多数简单场景,synchronized关键字是最直接、最简单的选择;而对于需要更灵活控制锁的场景,则可以考虑使用ReentrantLock等高级同步机制。
Java中线程之间的协作主要可以通过多种机制实现,其中等待/通知机制(wait/notify/notifyAll
)和join
方法是两种常用的方式。下面我将分别给出这两种方式的简单代码示例。
等待/通知机制依赖于Java中的Object
类,因为wait()
, notify()
, 和 notifyAll()
方法都定义在Object
类中。这些方法必须在同步块或同步方法中被调用,因为它们是用来控制对某个对象的访问的。
示例代码:
public class WaitNotifyExample {
private final Object lock = new Object();
private boolean ready = false;
public void doWait() {
synchronized (lock) {
while (!ready) {
try {
lock.wait(); // 当前线程等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 保持中断状态
}
}
// 当ready为true时,继续执行
}
}
public void doNotify() {
synchronized (lock) {
ready = true;
lock.notify(); // 唤醒在此对象监视器上等待的单个线程
// 或者使用 lock.notifyAll(); 唤醒所有等待的线程
}
}
public static void main(String[] args) {
WaitNotifyExample example = new WaitNotifyExample();
Thread t1 = new Thread(() -> {
System.out.println("Thread 1 is waiting");
example.doWait();
System.out.println("Thread 1 is proceeding");
});
Thread t2 = new Thread(() -> {
try {
Thread.sleep(1000); // 假设t2需要一些时间来完成准备工作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread 2 is notifying");
example.doNotify();
});
t1.start();
t2.start();
}
}
在这个例子中,t1
线程在doWait()
方法中等待,直到t2
线程调用doNotify()
方法并设置ready
为true
。t2
线程模拟了一些准备工作,并在之后唤醒t1
。
join
方法是Thread
类的一个方法,用于让当前线程等待另一个线程完成其执行。
示例代码:
public class JoinExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000); // 假设t1执行需要一些时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread 1 completed");
});
t1.start();
try {
t1.join(); // 当前线程(main线程)等待t1完成
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread 1 has joined, continuing main thread");
}
}
在这个例子中,main
线程启动了一个新线程t1
,并通过调用t1.join()
等待t1
完成。t1
线程在完成后会打印一条消息,而main
线程会在t1
完成后继续执行并打印另一条消息。
总结一下,我们讲了让如下内容