Java多线程是面试和实际开发中非常重要的内容。以下是一些常见的Java多线程八股文问题和场景题,以及详细答案和示例代码。
答案:
主要有以下几种方式:
Thread
类:重写run()
方法,通过start()
启动线程。Runnable
接口:实现run()
方法,通过Thread
类启动线程。Callable
接口:通过FutureTask
包装,支持返回值和异常处理。ExecutorService
管理线程,避免频繁创建和销毁线程。CompletableFuture
:支持异步编程,可以组合多个异步任务。示例代码:
// 继承Thread
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread via Thread class: " + Thread.currentThread().getName());
}
}
// 实现Runnable
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread via Runnable: " + Thread.currentThread().getName());
}
}
// 实现Callable
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Thread via Callable: " + Thread.currentThread().getName();
}
}
public class ThreadCreation {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 继承Thread
new MyThread().start();
// 实现Runnable
new Thread(new MyRunnable()).start();
// 实现Callable
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
答案:
Vector
、Hashtable
、ConcurrentHashMap
等。ArrayList
、HashMap
等。synchronized
)。ReentrantLock
)。ConcurrentHashMap
)。AtomicInteger
)。示例代码:
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadSafety {
private static int count = 0; // 线程不安全
private static AtomicInteger atomicCount = new AtomicInteger(0); // 线程安全
public static void increment() {
count++;
atomicCount.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> increment());
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
System.out.println("Non-atomic count: " + count); // 可能不等于1000
System.out.println("Atomic count: " + atomicCount.get()); // 一定等于1000
}
}
synchronized
和ReentrantLock
的区别?答案:
synchronized
:
ReentrantLock
:
示例代码:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizedVsReentrantLock {
private static final Object lock = new Object();
private static final Lock reentrantLock = new ReentrantLock();
public static void synchronizedExample() {
synchronized (lock) {
System.out.println("Synchronized block: " + Thread.currentThread().getName());
}
}
public static void reentrantLockExample() {
reentrantLock.lock();
try {
System.out.println("ReentrantLock block: " + Thread.currentThread().getName());
} finally {
reentrantLock.unlock();
}
}
public static void main(String[] args) {
new Thread(SynchronizedVsReentrantLock::synchronizedExample).start();
new Thread(SynchronizedVsReentrantLock::reentrantLockExample).start();
}
}
ReentrantLock
是 Java 中提供的一种可重入锁,它是 java.util.concurrent.locks
包中的一个重要同步工具。与传统的 synchronized
相比,ReentrantLock
提供了更灵活的锁操作,包括中断、超时和尝试锁定等功能。这些特性使得 ReentrantLock
在复杂的并发场景中更加适用。
以下是对 ReentrantLock
的详细讲解,包括中断、超时和尝试锁定的使用方法。
ReentrantLock
是一种可重入的互斥锁,支持多个线程对共享资源的互斥访问。它与 synchronized
的主要区别在于:
ReentrantLock
是基于 java.util.concurrent
包实现的,而 synchronized
是基于 JVM 的内置锁。ReentrantLock
提供了更丰富的锁操作,如尝试锁定、超时锁定、中断等待锁等。ReentrantLock
支持公平锁和非公平锁(默认是非公平锁)。ReentrantLock
的使用方式如下:
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
ReentrantLock
支持线程在等待锁时被中断。这可以通过 lockInterruptibly()
方法实现。
import java.util.concurrent.locks.ReentrantLock;
public class InterruptibleLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void doWork() throws InterruptedException {
lock.lockInterruptibly(); // 可中断的获取锁
try {
// 执行任务
System.out.println("任务正在执行,线程:" + Thread.currentThread().getName());
Thread.sleep(2000);
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) throws InterruptedException {
InterruptibleLockExample example = new InterruptibleLockExample();
Thread t1 = new Thread(() -> {
try {
example.doWork();
} catch (InterruptedException e) {
System.out.println("线程被中断:" + Thread.currentThread().getName());
}
});
t1.start();
Thread.sleep(1000); // 等待一段时间后中断线程
t1.interrupt();
}
}
任务正在执行,线程:Thread-0
线程被中断:Thread-0
说明:
lockInterruptibly()
方法在获取锁时可以响应中断。如果线程在等待锁时被中断,会抛出 InterruptedException
。ReentrantLock
提供了 tryLock(long timeout, TimeUnit unit)
方法,允许线程在尝试获取锁时设置超时时间。如果在指定时间内无法获取锁,线程可以放弃等待。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TimeoutLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void doWork() throws InterruptedException {
// 尝试在 1 秒内获取锁
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
System.out.println("获取锁成功,线程:" + Thread.currentThread().getName());
// 执行任务
Thread.sleep(2000);
} finally {
lock.unlock(); // 释放锁
}
} else {
System.out.println("获取锁超时,线程:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
TimeoutLockExample example = new TimeoutLockExample();
Thread t1 = new Thread(() -> {
try {
example.doWork();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
example.doWork();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread.sleep(500); // 确保 t1 先获取锁
t2.start();
}
}
获取锁成功,线程:Thread-0
获取锁超时,线程:Thread-1
说明:
tryLock(long timeout, TimeUnit unit)
方法允许线程在指定时间内尝试获取锁。如果在超时时间内获取到锁,则继续执行;否则返回 false
。ReentrantLock
提供了 tryLock()
方法,允许线程尝试获取锁,但不会阻塞。如果锁已经被其他线程持有,tryLock()
方法会立即返回 false
。
import java.util.concurrent.locks.ReentrantLock;
public class TryLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void doWork() {
if (lock.tryLock()) { // 尝试获取锁
try {
System.out.println("获取锁成功,线程:" + Thread.currentThread().getName());
// 执行任务
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
} else {
System.out.println("获取锁失败,线程:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
TryLockExample example = new TryLockExample();
Thread t1 = new Thread(() -> example.doWork());
Thread t2 = new Thread(() -> example.doWork());
t1.start();
Thread.sleep(500); // 确保 t1 先获取锁
t2.start();
}
}
获取锁成功,线程:Thread-0
获取锁失败,线程:Thread-1
说明:
tryLock()
方法尝试获取锁,但不会阻塞线程。如果锁可用,则立即获取锁并返回 true
;否则返回 false
。ReentrantLock
是可重入的,即同一个线程可以多次获取同一把锁。每次获取锁时,锁的计数器会递增;每次释放锁时,计数器会递减。当计数器为 0 时,锁被完全释放。
ReentrantLock
支持公平锁和非公平锁:
ReentrantLock lock = new ReentrantLock(true); // 公平锁
ReentrantLock lock = new ReentrantLock(false); // 非公平锁(默认)
ReentrantLock
是一种功能强大的锁机制,提供了以下特性:
ReentrantLock
的这些特性使得它在复杂的并发场景中更加灵活和强大,但同时也需要开发者谨慎使用,以避免死锁和性能问题。
答案:
ThreadPoolExecutor
是 Java 中用于创建和管理线程池的核心类,提供了更灵活的线程池配置和管理功能。使用 ThreadPoolExecutor
创建多线程时,需要注意一些关键细节,以确保线程池的性能、资源利用和线程安全。
以下是使用 ThreadPoolExecutor
时需要注意的细节:
ThreadPoolExecutor
的构造函数需要多个参数,这些参数的配置直接影响线程池的性能和资源占用。
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
);
corePoolSize
(核心线程数)
corePoolSize
可以设置为 CPU 核心数 + 1
。maximumPoolSize
(最大线程数)
maximumPoolSize
时,线程池会继续创建新线程。keepAliveTime
和 unit
(空闲线程存活时间)
corePoolSize
,空闲线程会在指定的时间后被销毁。workQueue
(任务队列)
ArrayBlockingQueue
:有界队列,适合固定大小的线程池。LinkedBlockingQueue
:无界队列,适合缓存线程池。SynchronousQueue
:直接提交队列,适合工作窃取线程池。PriorityBlockingQueue
:优先级队列,适合有优先级的任务。threadFactory
(线程工厂)
ThreadPoolExecutor
使用默认的线程工厂,但可以通过自定义线程工厂来设置线程的名称、优先级等属性。handler
(拒绝策略)
AbortPolicy
:抛出 RejectedExecutionException
。CallerRunsPolicy
:由提交任务的线程执行任务。DiscardPolicy
:丢弃任务。DiscardOldestPolicy
:丢弃队列中最老的任务。任务队列的选择对线程池的性能和行为有重要影响:
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
线程池使用完毕后,需要正确关闭,以释放资源。可以通过以下方法关闭线程池:
shutdown()
shutdownNow()
corePoolSize
和 maximumPoolSize
。ArrayBlockingQueue
),避免任务队列占用过多内存。ThreadPoolExecutor
提供的方法(如 getActiveCount()
、getCompletedTaskCount()
等)监控线程池的运行状态。ThreadLocal
),需要确保在任务结束时清理这些变量。ThreadPoolExecutor
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 200; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("任务 " + taskId + " 正在执行,线程:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}
使用 ThreadPoolExecutor
时,需要注意以下细节:
通过合理配置和使用 ThreadPoolExecutor
,可以显著提高多线程程序的性能和稳定性。
答案:
线程间的通信是多线程编程中的一个重要问题,它涉及到线程之间如何传递信息、协调操作以及同步状态。在 Java 中,提供了多种机制来解决线程间的通信问题,包括内置的同步机制、显式的线程通信工具以及高级并发工具类。
以下是解决线程间通信问题的几种常见方法:
wait()
和 notify()
/ notifyAll()
wait()
和 notify()
是 Java 中最基本的线程通信机制,它们依赖于对象的内置锁(synchronized
)。
wait()
:当前线程释放对象锁,并进入等待状态,直到其他线程调用该对象的 notify()
或 notifyAll()
方法。notify()
:唤醒一个正在等待该对象锁的线程。notifyAll()
:唤醒所有正在等待该对象锁的线程。public class ProducerConsumer {
private final Object lock = new Object();
private boolean available = false;
public void produce() throws InterruptedException {
synchronized (lock) {
while (available) { // 确保不会重复生产
lock.wait(); // 等待消费
}
// 生产操作
System.out.println("生产者生产数据");
available = true;
lock.notifyAll(); // 唤醒等待的线程
}
}
public void consume() throws InterruptedException {
synchronized (lock) {
while (!available) { // 确保有数据可消费
lock.wait(); // 等待生产
}
// 消费操作
System.out.println("消费者消费数据");
available = false;
lock.notifyAll(); // 唤醒等待的线程
}
}
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
Thread producer = new Thread(() -> {
try {
pc.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
pc.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
synchronized
块中使用。notify()
或 wait()
,可能导致线程死锁。Condition
接口Condition
是 Java java.util.concurrent.locks
包中的一个接口,用于替代传统的 wait()
和 notify()
。它提供了更灵活的线程通信机制。
Condition
与 Lock
配合使用。await()
(类似 wait()
)和 signal()
/ signalAll()
(类似 notify()
/ notifyAll()
)方法。import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerWithCondition {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean available = false;
public void produce() throws InterruptedException {
lock.lock();
try {
while (available) {
condition.await(); // 等待消费
}
// 生产操作
System.out.println("生产者生产数据");
available = true;
condition.signalAll(); // 唤醒等待的线程
} finally {
lock.unlock();
}
}
public void consume() throws InterruptedException {
lock.lock();
try {
while (!available) {
condition.await(); // 等待生产
}
// 消费操作
System.out.println("消费者消费数据");
available = false;
condition.signalAll(); // 唤醒等待的线程
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ProducerConsumerWithCondition pc = new ProducerConsumerWithCondition();
Thread producer = new Thread(() -> {
try {
pc.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
pc.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
wait()
和 notify()
更灵活。Lock
和 Condition
比内置锁更复杂。BlockingQueue
BlockingQueue
是一个线程安全的队列接口,提供了阻塞操作,使得线程间通信更加简单。它是解决生产者-消费者问题的推荐方式。
put()
方法将数据放入队列,如果队列已满,线程会阻塞。take()
方法从队列中取出数据,如果队列为空,线程会阻塞。import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumerWithBlockingQueue {
private final BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);
public void produce() throws InterruptedException {
String data = "数据";
System.out.println("生产者生产数据:" + data);
queue.put(data); // 放入队列,队列满时阻塞
}
public void consume() throws InterruptedException {
String data = queue.take(); // 从队列中取出数据,队列空时阻塞
System.out.println("消费者消费数据:" + data);
}
public static void main(String[] args) {
ProducerConsumerWithBlockingQueue pc = new ProducerConsumerWithBlockingQueue();
Thread producer = new Thread(() -> {
try {
pc.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
pc.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
CountDownLatch
CountDownLatch
是一个同步辅助工具,允许一个或多个线程等待其他线程完成操作。
countDown()
方法,计数值减 1。await()
方法的线程会阻塞,直到计数值为 0。import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); // 初始化计数器为 3
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 开始执行");
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 执行完成");
latch.countDown(); // 计数器减 1
}).start();
}
System.out.println("主线程等待所有子线程完成");
latch.await(); // 主线程阻塞,直到计数器为 0
System.out.println("所有子线程已完成,主线程继续执行");
}
}
CyclicBarrier
CyclicBarrier
是一个同步辅助工具,允许一组线程在某个点上相互等待。
await()
方法。import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3); // 设置屏障点的线程数为 3
答案:
死锁是指两个或多个线程互相等待对方释放资源,导致无法继续执行。避免死锁的方法:
tryLock()
尝试加锁,超时则放弃。答案:
在 Java 中,synchronized
关键字用于同步方法或代码块,以确保在多线程环境下对共享资源的互斥访问。synchronized
的作用范围取决于它修饰的对象:
在你的问题中,A
类中的 update
方法被 synchronized
修饰,且没有明确指出是静态方法,因此可以推断它是实例方法。
update
是实例方法,synchronized
锁的是当前对象实例(this
)。A1
和 A2
,它们分别调用 update
方法时:
A1
调用 update
时,锁的是 A1
对象。A2
调用 update
时,锁的是 A2
对象。因为 A1
和 A2
是两个不同的对象实例,它们的锁是独立的,互不干扰。因此,A1
和 A2
调用 update
方法时不会竞争锁资源。
class A {
public synchronized void update() {
System.out.println("更新方法被调用,当前线程:" + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新方法结束,当前线程:" + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
A A1 = new A();
A A2 = new A();
// 创建两个线程分别调用 A1 和 A2 的 update 方法
Thread t1 = new Thread(() -> A1.update(), "Thread-1");
Thread t2 = new Thread(() -> A2.update(), "Thread-2");
t1.start();
t2.start();
}
}
更新方法被调用,当前线程:Thread-1
更新方法被调用,当前线程:Thread-2
更新方法结束,当前线程:Thread-1
更新方法结束,当前线程:Thread-2
从输出可以看出,A1
和 A2
的 update
方法可以同时运行,互不干扰。
update
是静态方法:如果 update
方法是静态的(static synchronized
),情况会有所不同。静态方法的锁是类对象(A.class
),而不是实例对象。因此,无论 A1
还是 A2
调用静态的 update
方法,它们都会竞争同一个锁(类锁),从而导致线程互斥。
class A {
public static synchronized void update() {
System.out.println("更新方法被调用,当前线程:" + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新方法结束,当前线程:" + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
A A1 = new A();
A A2 = new A();
Thread t1 = new Thread(() -> A.update(), "Thread-1");
Thread t2 = new Thread(() -> A.update(), "Thread-2");
t1.start();
t2.start();
}
}
更新方法被调用,当前线程:Thread-1
更新方法结束,当前线程:Thread-1
更新方法被调用,当前线程:Thread-2
更新方法结束,当前线程:Thread-2
从输出可以看出,Thread-2
必须等待 Thread-1
完成后才能执行,说明它们竞争同一个锁。
update
是实例方法,A1
和 A2
调用时不会竞争锁资源。update
是静态方法,A1
和 A2
调用时会竞争同一个锁(类锁)。当 synchronized
修饰的是代码块时,锁的作用范围取决于代码块中指定的对象。synchronized
代码块可以锁定以下两种对象:
this
):锁定当前对象实例。this
)如果 synchronized
代码块锁定的是当前实例对象(this
),那么行为与同步实例方法类似。每个对象实例都有自己的锁,不同实例的锁是独立的。
class A {
public void update() {
synchronized (this) { // 锁定当前实例对象
System.out.println("更新方法被调用,当前线程:" + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新方法结束,当前线程:" + Thread.currentThread().getName());
}
}
}
public class Main {
public static void main(String[] args) {
A A1 = new A();
A A2 = new A();
Thread t1 = new Thread(() -> A1.update(), "Thread-1");
Thread t2 = new Thread(() -> A2.update(), "Thread-2");
t1.start();
t2.start();
}
}
更新方法被调用,当前线程:Thread-1
更新方法被调用,当前线程:Thread-2
更新方法结束,当前线程:Thread-1
更新方法结束,当前线程:Thread-2
分析:
A1
和 A2
是不同的实例,它们各自锁定自己的 this
对象。Thread-1
和 Thread-2
不会竞争锁资源,可以同时运行。如果 synchronized
代码块锁定的是同一个共享对象,那么所有线程都会竞争同一个锁。
class A {
private static final Object lock = new Object(); // 共享锁对象
public void update() {
synchronized (lock) { // 锁定同一个共享对象
System.out.println("更新方法被调用,当前线程:" + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新方法结束,当前线程:" + Thread.currentThread().getName());
}
}
}
public class Main {
public static void main(String[] args) {
A A1 = new A();
A A2 = new A();
Thread t1 = new Thread(() -> A1.update(), "Thread-1");
Thread t2 = new Thread(() -> A2.update(), "Thread-2");
t1.start();
t2.start();
}
}
更新方法被调用,当前线程:Thread-1
更新方法结束,当前线程:Thread-1
更新方法被调用,当前线程:Thread-2
更新方法结束,当前线程:Thread-2
分析:
A1
和 A2
的 update
方法都锁定的是同一个共享对象 lock
。Thread-1
和 Thread-2
会竞争同一个锁,导致互斥。A.class
)如果 synchronized
代码块锁定的是类对象(A.class
),那么行为与同步静态方法类似。所有线程都会竞争同一个类锁。
class A {
public void update() {
synchronized (A.class) { // 锁定类对象
System.out.println("更新方法被调用,当前线程:" + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新方法结束,当前线程:" + Thread.currentThread().getName());
}
}
}
public class Main {
public static void main(String[] args) {
A A1 = new A();
A A2 = new A();
Thread t1 = new Thread(() -> A1.update(), "Thread-1");
Thread t2 = new Thread(() -> A2.update(), "Thread-2");
t1.start();
t2.start();
}
}
更新方法被调用,当前线程:Thread-1
更新方法结束,当前线程:Thread-1
更新方法被调用,当前线程:Thread-2
更新方法结束,当前线程:Thread-2
分析:
A1
和 A2
的 update
方法都锁定的是同一个类对象 A.class
。Thread-1
和 Thread-2
会竞争同一个锁,导致互斥。synchronized
代码块锁定的是 当前实例对象(this
),不同实例的锁是独立的,不会竞争锁资源。synchronized
代码块锁定的是 同一个共享对象 或 类对象(A.class
),所有线程会竞争同一个锁,导致互斥。通过合理选择锁对象,可以灵活控制线程之间的同步行为。