【问题标题】:Java synchronization: Lock without blockingJava同步:无阻塞锁定
【发布时间】:2013-01-16 21:40:06
【问题描述】:

我有一个同步练习,我需要同步一个读取方法,以便只要不执行任何写入方法,任意数量的线程都可以执行它。这必须从头开始,所以我不能使用 java.util.concurrent.locks 等。

为此,我需要某种机制来保护,但不阻塞读取方法,因此读取线程被写入阻塞,但不会被其他读取阻塞。我不能为此使用普通锁,因为在 read 方法中调用 lock 方法会导致其他读取线程等待。

规则应该是这样的: 当一个线程在 write() 中时,没有其他线程必须进入 read() 或 write() 当一个线程在read()里面时,其他线程都不能进入write(),但是可以进入read()

我已经尝试建造几个自制的锁来解决这个问题。 WriteLock 是一个相当标准的可重入锁,除了如果正在执行读取它会阻塞(使用 readcounter) 如果正在遍历 write(),则 ReadLock 应该只导致线程等待。否则它应该简单地允许线程继续其业务并增加 WriteLocks 计数器。

代码:

package sync;

public class SyncTest {
    Long testlong = new Long(0L);
    int reads = 0;
    int writes = 0;
    WriteLock w = new WriteLock();
    ReadLock r = new ReadLock(w);

    public SyncTest() {
        // TODO Auto-generated constructor stub
    }

    public static void main(String args[]){

        final SyncTest s = new SyncTest();

        for(int i = 0 ; i<3 ; i++){ //Start a number of threads to attack SyncTest
            final int ifinal = i;
            new Thread(){
                int inc = ifinal;
                @Override
                public void run() {
                    System.out.println("Starting "+inc);
                    long starttime = System.currentTimeMillis();
                    try {
                    while(System.currentTimeMillis()-starttime < 10){

                        if (inc < 2){

                            s.readLong();

                        }else{
                            s.writeLong(inc+1);
                        }
                    }
                    System.out.println(inc + " done");
                    if(inc == 0){
                        Thread.sleep(1000);
                        System.out.println(s.reads+" "+s.writes);
                    }
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    // TODO Auto-generated method stub

                }
                @Override
                public String toString() {
                    // TODO Auto-generated method stub
                    return "Thread "+inc+" "+super.toString();
                }



            }.start();
        }
    }

    public Long readLong() throws InterruptedException{

        Long l;
        r.lock(); //Lock for reading
        //System.out.println("Read "+reads);
        l =  testlong;
        reads++;
        r.unlock(); //Unlock after reading
        return l;   
        }

    public void writeLong(int i) throws InterruptedException{

        w.lock(); //Lock for writing
        //System.out.println("Write "+writes);
        int curreads = reads;
        int curwrites = writes;
        testlong = testlong + i;
        writes++;

        Thread.sleep(100); //Simulate long write
        if(curreads != reads){
            System.out.println("Reads did not lock");
        }

        if(curwrites+1 != writes){
            System.out.println("Writes did not lock");
        }
        w.unlock(); //Unlock writing
    }

    protected class WriteLock{
        boolean isLocked = false;
        Thread lockedBy = null;
        int lockedCount = 0;
        int readers = 0; //The number of readers currently through the reading lock.

        public synchronized void lock() throws InterruptedException {
            System.out.println("Locking: "+Thread.currentThread());
            Thread callingThread = Thread.currentThread();
            while ((isLocked && lockedBy != callingThread) || readers > 0) { //Wait if locked or readers are in read()
                wait();
            }
            isLocked = true;
            lockedCount++;
            lockedBy = callingThread;
            System.out.println("Is locked: "+Thread.currentThread());
        }

        public synchronized void unlock() {
            //System.out.println("Unlocking: "+Thread.currentThread());
            if (Thread.currentThread() == this.lockedBy) {
                lockedCount--;

                if (lockedCount == 0) {
                    System.out.println("Is unlocked: "+Thread.currentThread());
                    isLocked = false;
                    notify();
                }
            }
        }

    }

    protected class ReadLock{
        WriteLock lock;

        public ReadLock(WriteLock lock) {
            super();
            this.lock = lock;
        }

        public synchronized void lock() throws InterruptedException { //If write() then wait
            System.out.println("Waiting to read: "+Thread.currentThread());
            Thread callingThread = Thread.currentThread();
            while (lock.isLocked && lock.lockedBy != callingThread) {
                wait();
            }
            lock.readers++; //Increment writelocks readers
            System.out.println("Reading: "+Thread.currentThread());

        }

        public synchronized void unlock() {
            lock.readers--; //Subtract from writelocks readers
            notify();
        }

    }

}

但是,这不起作用,读取锁的工作方式到目前为止它会在线程写入时锁定读取器,但据我所知,当 WriteLock 解锁时它不会释放它们。

这只是在概念上不合理,还是我对显示器有什么不明白的地方?还是别的什么?

【问题讨论】:

  • 我建议您将try {} finally {} 包裹在您的锁上。如果在锁定过程中出现任何异常,您的代码将无法解锁。
  • 非常正确。但这只是一个简短的编译,我不想弄乱代码。不会抛出任何错误。

标签: java concurrency locking


【解决方案1】:

(在问题被编辑之前回答,因为它是一个练习。)

听起来你想要一个ReadWriteLock 实现。

ReadWriteLock 维护一对关联的锁,一个用于只读操作,一个用于写入。只要没有写者,读锁可能被多个读线程同时持有。写锁是独占的。

一个实现是ReentrantReadWriteLock

特别是对于并发性,在尝试实现自己的代码之前,总是值得查看现有的库(java.util.concurrent 等)。如果您像我一样,即使您可以在并发方面正确,它也不会像专家编写的代码那样高效......当然这一切都是为了从;)开始

【讨论】:

  • 除非您想将读写器锁定作为学习练习,否则您应该只使用 Jon 建议的 java 库附带的那个。
  • @MartinNielsen:你应该在你的问题中这么说。你说你有一个同步问题。 ReadWriteLock 是该同步问题的解决方案。你没有说必须从第一原则实施它。
  • 不能再同意这个了。我知道很多人都患有此处未发明综合症,但并发性已经够难了;我们不需要通过使用非战斗硬化库来使其变得更难。 (是的,这家伙碰巧要从头开始写,但在这里搜索的很多人可能不会。)
【解决方案2】:

您的 ReadLock 和 WriteLock 在不同的对象上同步,并在不同的对象上调用 wait 和 notify。

这允许 ReadLock 在 WriteLock 验证计数时修改 WriteLock 中的计数。它还会导致不同的锁无法从等待调用中唤醒。

如果您修改 ReadLock 以将 WriteLock 用作监视器,您将获得更好的结果(我没有检查这是否是唯一的问题)

protected class ReadLock{
    WriteLock lock;

    public ReadLock(WriteLock lock) {
        super();
        this.lock = lock;
    }

    public void lock() throws InterruptedException { //If write() then wait
        synchronized (lock) {
           System.out.println("Waiting to read: "+Thread.currentThread());
           Thread callingThread = Thread.currentThread();
           while (lock.isLocked && lock.lockedBy != callingThread) {
               lock.wait();
           }
           lock.readers++; //Increment writelocks readers
           System.out.println("Reading: "+Thread.currentThread());
       }
    }

    public void unlock() {
        synchronized (lock) {
           lock.readers--; //Subtract from writelocks readers
           lock.notify();
        }
    }

}

【讨论】:

    猜你喜欢
    • 2018-04-06
    • 1970-01-01
    • 2016-07-14
    • 2023-03-26
    • 2019-10-10
    • 1970-01-01
    • 2023-03-14
    • 2013-07-20
    相关资源
    最近更新 更多