面试题答案
一键面试多线程环境下Java多态和动态绑定的特殊问题
- 可见性问题:不同线程操作具有多态特性的对象时,一个线程对对象状态的修改,另一个线程可能无法及时看到。例如,一个线程修改了对象的某个属性,该属性在重写方法中被使用,而其他线程调用重写方法时可能使用的还是旧的属性值。
- 竞态条件:多个线程同时调用对象的重写方法,如果这些方法涉及对共享资源的读写操作,就可能出现竞态条件。比如多个线程同时调用一个重写的
update
方法,该方法会修改对象内部的共享数据,可能导致数据不一致。
可能出现的情况示例
假设存在以下类层次结构:
class Animal {
protected int health;
public Animal(int health) {
this.health = health;
}
public void displayHealth() {
System.out.println("Animal's health: " + health);
}
}
class Dog extends Animal {
public Dog(int health) {
super(health);
}
@Override
public void displayHealth() {
System.out.println("Dog's health: " + health);
}
}
在多线程环境下:
public class PolymorphismThread implements Runnable {
private Animal animal;
public PolymorphismThread(Animal animal) {
this.animal = animal;
}
@Override
public void run() {
// 假设这里是对动物健康的一些操作,可能会出现竞态条件
animal.health--;
animal.displayHealth();
}
}
当多个 PolymorphismThread
线程同时操作 Dog
对象时,可能会出现 health
值的不一致,因为 animal.health--
不是原子操作。
解决方案
- 使用
synchronized
关键字:- 可以在重写方法上使用
synchronized
关键字,确保同一时间只有一个线程能进入该方法。
class Dog extends Animal { public Dog(int health) { super(health); } @Override public synchronized void displayHealth() { System.out.println("Dog's health: " + health); } }
- 也可以对共享资源的操作进行同步块处理:
@Override public void run() { synchronized (animal) { animal.health--; animal.displayHealth(); } }
- 可以在重写方法上使用
- 使用
Lock
接口:Lock
接口提供了比synchronized
更灵活的同步控制。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Dog extends Animal { private Lock lock = new ReentrantLock(); public Dog(int health) { super(health); } @Override public void displayHealth() { lock.lock(); try { System.out.println("Dog's health: " + health); } finally { lock.unlock(); } } }
- 在多线程操作中:
@Override public void run() { lock.lock(); try { animal.health--; animal.displayHealth(); } finally { lock.unlock(); } }
- 使用线程安全的类和设计模式:
- 例如使用
AtomicInteger
代替int
来保证原子性操作。
import java.util.concurrent.atomic.AtomicInteger; class Animal { protected AtomicInteger health; public Animal(int health) { this.health = new AtomicInteger(health); } public void displayHealth() { System.out.println("Animal's health: " + health.get()); } } class Dog extends Animal { public Dog(int health) { super(health); } @Override public void displayHealth() { System.out.println("Dog's health: " + health.get()); } }
- 在多线程操作中:
@Override public void run() { health.decrementAndGet(); animal.displayHealth(); }
- 还可以考虑使用
Immutable
对象模式,确保对象状态不可变,避免多线程修改带来的问题。例如:
class ImmutableAnimal { private final int health; public ImmutableAnimal(int health) { this.health = health; } public ImmutableAnimal updateHealth(int newHealth) { return new ImmutableAnimal(newHealth); } public void displayHealth() { System.out.println("Animal's health: " + health); } }
- 在多线程中:
@Override public void run() { ImmutableAnimal newAnimal = animal.updateHealth(animal.health - 1); newAnimal.displayHealth(); }
- 例如使用
通过以上同步机制和设计模式,可以有效避免多线程环境下Java多态和动态绑定带来的问题。