Java 如何在读取时阻止线程对数组的写访问

Java 如何在读取时阻止线程对数组的写访问,java,multithreading,concurrency,Java,Multithreading,Concurrency,我有两个并行运行的线程,为了获得关于它们内部结果的信息,我创建了长度为8的int数组。关于他们的id,他们可以更新statu数组上的相对区域。他们不允许在其他区域写文章。此外,为了正确获取和显示statu数组,我尝试编写getStatu方法。在得到结果时,我想阻止其他人写入statu数组;不幸的是,当我在getStatu方法中获取并显示结果时,我不知道如何阻止他人写入statu数组。怎么做 注意:如果有一个部分引起误解,告诉我我的朋友,我会修复 class A{ Semaphore se

我有两个并行运行的线程,为了获得关于它们内部结果的信息,我创建了长度为8的int数组。关于他们的id,他们可以更新statu数组上的相对区域。他们不允许在其他区域写文章。此外,为了正确获取和显示statu数组,我尝试编写getStatu方法。在得到结果时,我想阻止其他人写入statu数组;不幸的是,当我在getStatu方法中获取并显示结果时,我不知道如何阻止他人写入statu数组。怎么做

注意:如果有一个部分引起误解,告诉我我的朋友,我会修复

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();
    }
}