面试题答案
一键面试多线程环境下Java命令模式面临的挑战
- 线程安全问题:在多线程环境中,如果多个线程同时访问和执行同一个命令对象,可能会导致数据不一致。例如,命令对象内部维护了一些状态变量,不同线程对这些变量的操作顺序和时机不同,就可能引发错误。
- 资源竞争:当多个命令需要访问共享资源(如文件、数据库连接等)时,会出现资源竞争问题。如果没有适当的同步机制,可能导致数据损坏或程序异常。
改进设计思路
- 使用线程安全的数据结构:在命令对象中,如果需要维护状态,使用线程安全的数据结构,如
ConcurrentHashMap
、CopyOnWriteArrayList
等。 - 同步机制:对共享资源的访问使用
synchronized
关键字或者Lock
接口进行同步控制,确保同一时间只有一个线程可以访问共享资源。 - 命令队列与线程池:将命令放入队列中,使用线程池来处理队列中的命令,这样可以控制并发度,避免过多线程同时执行命令导致资源耗尽。
关键代码示例
- 定义命令接口
public interface Command {
void execute();
}
- 具体命令类
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConcreteCommand implements Command {
private static int sharedResource = 0;
private static Lock lock = new ReentrantLock();
@Override
public void execute() {
lock.lock();
try {
// 访问和修改共享资源
sharedResource++;
System.out.println("Command executed, sharedResource: " + sharedResource);
} finally {
lock.unlock();
}
}
}
- 调用者(Invoker)
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Invoker {
private BlockingQueue<Command> commandQueue;
private ThreadPoolExecutor executor;
public Invoker(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
commandQueue = new LinkedBlockingQueue<>();
executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
commandQueue);
}
public void addCommand(Command command) {
commandQueue.add(command);
}
public void start() {
executor.prestartAllCoreThreads();
}
public void stop() {
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
- 测试代码
public class Main {
public static void main(String[] args) {
Invoker invoker = new Invoker(2, 4, 10, TimeUnit.SECONDS);
for (int i = 0; i < 10; i++) {
invoker.addCommand(new ConcreteCommand());
}
invoker.start();
invoker.stop();
}
}
在上述代码中,ConcreteCommand
类通过Lock
接口保证了对共享资源sharedResource
的线程安全访问。Invoker
类使用ThreadPoolExecutor
和BlockingQueue
来管理命令的执行,控制并发度,从而适应多线程环境。