我在采访中被问及以下问题。多线程 - 共享数据
有一个对象被多个线程共享。该对象具有以下功能。如何确保不同的线程可以同时为不同的参数x值执行函数?如果两个线程正在执行相同的x值,则应阻止其中一个线程。
public void func(String x){
-----
}
“同步”的关键字不会在这种情况下工作,因为它会确保只有一个线程可以同时执行。请让我知道什么将会成为这个
我在采访中被问及以下问题。多线程 - 共享数据
有一个对象被多个线程共享。该对象具有以下功能。如何确保不同的线程可以同时为不同的参数x值执行函数?如果两个线程正在执行相同的x值,则应阻止其中一个线程。
public void func(String x){
-----
}
“同步”的关键字不会在这种情况下工作,因为它会确保只有一个线程可以同时执行。请让我知道什么将会成为这个
,想到的第一件事情解决的办法是像
public void func(String x){
synchronized (x.intern()) {
// Body here
}
}
这将表现就像描述;当然,这感觉就像一个讨厌的黑客,因为被同步的对象是公开的,其他代码可能会因此而干扰锁定。
此解决方案的另一个问题是:它不适用于x的空值。 – 2013-02-21 05:55:17
@EyalSchneider,好点 - 虽然根据完整的规范,抛出'NullPointerException'可能是对null参数的完美回应。 – 2013-02-21 05:57:46
创建一个HashMap作为成员变量。
private HashMap<String,Lock> lockMap = new HashMap<String,Lock>();
public void func(String x){
if(lockMap.get(x) == null){
lockMap.put(x,new ReentrantLock());
}
lockMap.get(x).lock();
...
...
lockMap.get(x).unlock();
}
大概是面试官对Ernest Friedman-Hill提出的解决方案感兴趣。但是,由于其缺点,它通常不能用于生产代码。有一次,我写了下面的同步工具来处理这个问题:
package com.paypal.risk.ars.dss.framework.concurrent;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
public class MultiLock <K>{
private ConcurrentHashMap<K, ReentrantLock> locks = new ConcurrentHashMap<K, ReentrantLock>();
/**
* Locks on a given id.
* Make sure to call unlock() afterwards, otherwise serious bugs may occur.
* It is strongly recommended to use try{ }finally{} in order to guarantee this.
* Note that the lock is re-entrant.
* @param id The id to lock on
*/
public void lock(K id) {
while (true) {
ReentrantLock lock = getLockFor(id);
lock.lock();
if (locks.get(id) == lock)
return;
else // means that the lock has been removed from the map by another thread, so it is not safe to
// continue with the one we have, and we must retry.
// without this, another thread may create a new lock for the same id, and then work on it.
lock.unlock();
}
}
/**
* Unlocks on a given id.
* If the lock is not currently held, an exception is thrown.
*
* @param id The id to unlock
* @throws IllegalMonitorStateException in case that the thread doesn't hold the lock
*/
public void unlock(K id) {
ReentrantLock lock = locks.get(id);
if (lock == null || !lock.isHeldByCurrentThread())
throw new IllegalMonitorStateException("Lock for " + id + " is not owned by the current thread!");
locks.remove(id);
lock.unlock();
}
private ReentrantLock getLockFor(K id) {
ReentrantLock lock = locks.get(id);
if (lock == null) {
lock = new ReentrantLock();
ReentrantLock prevLock = locks.putIfAbsent(id, lock);
if (prevLock != null)
lock = prevLock;
}
return lock;
}
}
注意,它可以更简单地用一个简单的地图和一个全局锁来实现。但是,为了提高吞吐量,我想避免全局锁定。
我的KISS感觉让我告诉你只要使方法“同步”,并且在实际上成为瓶颈时回到发烧友同步。 – 2013-02-21 05:42:31
要点 - “你的问题很蠢”很少是面试官想听到的,但是! – 2013-02-21 05:46:51