面试题答案
一键面试确保类线程安全的常见方式
- 不可变对象:将类设计为不可变,一旦对象创建,其状态就不能被改变。例如
java.lang.String
类,所有的修改操作(如concat
)都会返回新的String
对象,而不是修改原对象。 - 使用
synchronized
关键字:- 修饰方法:当一个方法被
synchronized
修饰时,同一时间只有一个线程可以访问该方法。例如,下面的Counter
类中的increment
方法被synchronized
修饰,保证了多线程环境下count
变量自增操作的线程安全。
public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }
- 修饰代码块:可以使用
synchronized
关键字修饰代码块,对特定的代码片段进行同步。比如在BankAccount
类的transfer
方法中,通过synchronized
块保证对balance
变量的操作线程安全。
public class BankAccount { private double balance; public BankAccount(double initialBalance) { this.balance = initialBalance; } public void transfer(BankAccount other, double amount) { synchronized (this) { synchronized (other) { if (this.balance >= amount) { this.balance -= amount; other.balance += amount; } } } } public double getBalance() { return balance; } }
- 修饰方法:当一个方法被
- 使用
java.util.concurrent
包中的并发工具:Atomic
类:例如AtomicInteger
,它提供了原子性的操作方法,底层使用了 CAS(Compare - and - Swap)算法。以AtomicCounter
类为例:
import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }
Lock
接口:Lock
接口提供了比synchronized
关键字更灵活的锁控制。如ReentrantLock
,在LockCounter
类中使用ReentrantLock
来保证count
变量自增操作的线程安全。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockCounter { private int count = 0; private Lock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } public int getCount() { return count; } }
实际代码应用举例(以 synchronized
修饰方法为例)
public class ThreadSafeExample {
private static class SharedResource {
private int data = 0;
public synchronized void updateData(int value) {
data = value;
}
public synchronized int getData() {
return data;
}
}
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
resource.updateData(i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
int value = resource.getData();
System.out.println("Thread 2 read: " + value);
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在上述代码中,SharedResource
类的 updateData
和 getData
方法都被 synchronized
修饰,确保了在多线程环境下对 data
变量的读写操作是线程安全的。main
方法中创建了两个线程,一个线程更新数据,另一个线程读取数据,通过 synchronized
方法保证了数据的一致性。