Java 如何在读取时阻止线程对数组的写访问
我有两个并行运行的线程,为了获得关于它们内部结果的信息,我创建了长度为8的int数组。关于他们的id,他们可以更新statu数组上的相对区域。他们不允许在其他区域写文章。此外,为了正确获取和显示statu数组,我尝试编写getStatu方法。在得到结果时,我想阻止其他人写入statu数组;不幸的是,当我在getStatu方法中获取并显示结果时,我不知道如何阻止他人写入statu数组。怎么做 注意:如果有一个部分引起误解,告诉我我的朋友,我会修复Java 如何在读取时阻止线程对数组的写访问,java,multithreading,concurrency,Java,Multithreading,Concurrency,我有两个并行运行的线程,为了获得关于它们内部结果的信息,我创建了长度为8的int数组。关于他们的id,他们可以更新statu数组上的相对区域。他们不允许在其他区域写文章。此外,为了正确获取和显示statu数组,我尝试编写getStatu方法。在得到结果时,我想阻止其他人写入statu数组;不幸的是,当我在getStatu方法中获取并显示结果时,我不知道如何阻止他人写入statu数组。怎么做 注意:如果有一个部分引起误解,告诉我我的朋友,我会修复 class A{ Semaphore se
class A{
Semaphore semaphore;
int [] statu; // statu is of length 8
void update(int idOfThread, int []statu_){
try {
semaphore.acquire();
int idx = idOfThread * 4;
statu[idx] = statu_[0];
statu[idx+1] = statu_[1];
statu[idx+2] = statu_[2];
statu[idx+3] = statu_[3];
} catch (...) {
} finally {
semaphore.release();
}
}
int[] getStatu(){
// Block write access of threads
// display statu array
// return statu array as return value
// release block, so threads can write to the array
}
}
除了使用另一种锁/snc机制(而不是信号量)之外,还有一个稍微改进的建议 将两个状态[4]数组放入单个数组[8]并不是最佳解决方案。考虑任务A编写它的四元组:它必须锁定任务B读取相同的内容,但是没有锁定B任务的写入B的四元组,反之亦然。
一般来说,被锁定内容的粒度是一个重要因素:锁定整个数据库是毫无意义的,除了像备份这样的整体处理,但是锁定记录的各个字段会产生过多的开销。可能有更好的方法来达到您想要的目的,但只有你知道你想做什么。按照你自己的计划,有些事情你做错了。首先,目前您还没有实现计划中的细粒度锁定。为此,必须有一个信号量数组。所以这次收购看起来像是
semaphore[idOfThread].acquire();
其次,有一件事您没有意识到,线程之间对数据的受控访问是一种协作活动。您不能锁定一个线程,而不关心如何锁定另一个线程,并以某种方式实施访问控制
因此,除非getStatu的调用方在检查数组时使用同一组信号量,否则getStatu最好创建一个新的int[]数组,在使用相应的信号量锁定后复制每个线程的段。因此getStatu返回的数组将是调用点的快照。请尝试下面的代码,它将适用于您。打电话给afterStatu
就像ac3所说的,只有你知道你想做什么 如果每个调用update的线程都非常频繁,并且很少调用getStatu,那么下面的解决方案可能会非常有用。这很复杂,但它允许大多数更新调用在没有任何锁定的情况下进行
static final int NUMBER_OF_WORKER_THREADS = ...;
final AtomicReference<CountDownLatch> pauseRequested = new AtomicReference<CountDownLatch>(null);
final Object lock = new Object();
int[] statu = ...
//called in "worker" thread.
void update() {
if (pauseRequested.get() != null) {
pause();
}
... update my slots in statu[] array ...
}
private void pause() {
notifyMasterThatIAmPaused();
waitForMasterToLiftPauseRequest();
}
private void notifyMasterThatIAmPaused() {
pauseRequested.get().countDown();
}
private void waitForMasterToLiftPauseRequest() {
synchronized(lock) {
while (pauseRequested.get() != null) {
lock.wait();
}
}
}
//called in "master" thread
int[] getStatu( ) {
int[] result;
CountDownLatch cdl = requestWorkersToPause();
waitForWorkersToPause(cdl);
result = Arrays.copyOf(statu, statu.length);
liftPauseRequest();
return result;
}
private CountDownLatch requestWorkersToPause() {
cdl = new CountDownLatch(NUMBER_OF_WORKER_THREADS);
pauseRequested.set(cdl);
return cdl;
}
private void waitForWorkersToPause(CountDownLatch cdl) {
cdl.await();
}
private void liftPauseRequest() {
synchronized(lock) {
pauseRequested.set(null);
lock.notifyAll();
}
}
您可以通过java.util.concurrent.ConcurrentHashMap完成这项工作。但也许你不应该。这样会引入大量争用并减少线程的并发性。。。是的。要么A是单态,要么Status和信号量是静态的。否则,您的线程将无法同步,因为它们无法共享公共基础结构。为什么不使用ReentrantLock?@blafasel您可以编写演示代码段,我想减少两个线程的并发性。事实上,我为什么要这么做?他们正在更新两个不同的地方。@prajeeshkumar我不知道ReentrantLock有什么帮助。使用ReentrantReadWriteLock。update方法将在rwLock.writeLock.lock下执行其工作,而getStatus将访问rwLock.readLock.lock下的数组。
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
int[] statu;
void update() {
lock.writeLock().lock();
try {
// update statu
} finally {
lock.writeLock().unlock();
}
}
int[] getStatu() {
lock.readLock().lock();
try {
// return statu
} finally {
lock.readLock().unlock();
}
}
static final int NUMBER_OF_WORKER_THREADS = ...;
final AtomicReference<CountDownLatch> pauseRequested = new AtomicReference<CountDownLatch>(null);
final Object lock = new Object();
int[] statu = ...
//called in "worker" thread.
void update() {
if (pauseRequested.get() != null) {
pause();
}
... update my slots in statu[] array ...
}
private void pause() {
notifyMasterThatIAmPaused();
waitForMasterToLiftPauseRequest();
}
private void notifyMasterThatIAmPaused() {
pauseRequested.get().countDown();
}
private void waitForMasterToLiftPauseRequest() {
synchronized(lock) {
while (pauseRequested.get() != null) {
lock.wait();
}
}
}
//called in "master" thread
int[] getStatu( ) {
int[] result;
CountDownLatch cdl = requestWorkersToPause();
waitForWorkersToPause(cdl);
result = Arrays.copyOf(statu, statu.length);
liftPauseRequest();
return result;
}
private CountDownLatch requestWorkersToPause() {
cdl = new CountDownLatch(NUMBER_OF_WORKER_THREADS);
pauseRequested.set(cdl);
return cdl;
}
private void waitForWorkersToPause(CountDownLatch cdl) {
cdl.await();
}
private void liftPauseRequest() {
synchronized(lock) {
pauseRequested.set(null);
lock.notifyAll();
}
}