【问题标题】:Simple Java code allow single thread access and other threads to skip/move on简单的 Java 代码允许单线程访问和其他线程跳过/继续
【发布时间】:2011-09-09 08:30:01
【问题描述】:

换句话说,如果线程无法访问锁(如同步),我不希望线程等待,如果无法获得锁,我希望线程执行在该点立即返回。

像这样简单的布尔锁可能会允许多个线程访问。

private static boolean lockAvailable = true;

private boolean acquireLock() {
  if(lockAvailable) {
    lockAvailable = false;
    return true;
  }
  return false;
}

我错过了什么吗?实现这一目标的最佳/最简单方法是什么?

编辑:

感谢您指出信号量(!)

所以再看一遍这段代码是防弹的吗?

private final static Semaphore lock = new Semaphore(1, true);   

public void tryAndDoSomething() {
  if(lock.tryAcquire()) {
    try {
      // only single thread can access here at one time
    } finally {
      lock.release();
    }
  }
}

更新:

我意识到我需要可重入功能,因此我创建了一个简单的非阻塞可重入。为任何对如何执行此操作感兴趣的人发布代码。任何想要这种功能的人当然应该使用现有的 Java 类 java.util.concurrent.locks.ReentrantLock :|

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * SimpleNonBlockingLock ensures that only a single thread can call protected code at any one time,
 * while allowing other threads to by pass the protected code if the lock is unavailable.
 * The thread owning the lock can access any code protected by the lock (the lock is 'reentrant').
 * To function correctly the protected code must be executed in a try/finally blocks. The
 * finally block must call the tryRelease. Example code:
 * 
 *  private final SimpleNonBlockingLock lock = new SimpleNonBlockingLock();
 * 
 *  if(lock.tryAcquire()) {
 *      try {
 *              // access protected code
 *      } finally {
 *          lock.tryRelease();
 *      }
 *  }
 *
 * This code is for demonstration only and should not be used. I have tested it and it 'seems to' work.
 * However it may contain horrific bugs!
 * 
 * The Java class java.util.concurrent.locks.ReentrantLock has been around since Java 5.0 and contains all (and more) 
 * of this functionality. Its also been thoroughly tested!
 */
public class SimpleNonBlockingLock {

    // Atomic locking mechanism
    private final AtomicBoolean locked = new AtomicBoolean();

    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // Unique ID of thread which currently has lock
    private int threadUniqueId = -1;

    // Tracks number of tryAcquire calls made by thread with lock
    private int lockCount = 0;

    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
            @Override protected Integer initialValue() {
                return nextId.getAndIncrement();
        }
    };

    public synchronized boolean tryAcquire() {      
        // Allow owning thread to acquire
        if(threadUniqueId == getCurrentThreadUniqueId()) {
            lockCount++;
            return true;
        }       
        // If locked then do not allow
        if (locked.get()) {return false;}           
        // Attempt to acquire lock      
        boolean attemptAcquire = locked.compareAndSet(false, true);     
        // If successful then set threadUniqueId for the thread, and increment lock count
        if(attemptAcquire) {
            threadUniqueId = getCurrentThreadUniqueId();
            lockCount++;
        }       
        // Return result of attempt to acquire lock
        return attemptAcquire;
    }

    public synchronized boolean tryRelease() {
        if (!locked.get()) {
            // Lock is currently available - no need to try and release
            return true;
        } else {
            // Decrement the lock count
            lockCount--;
            // If lock count is zero we release lock, and reset the threadUniqueId
            if(lockCount == 0) {
                threadUniqueId = -1;
                return locked.compareAndSet(true, false);
            } 
            return false;
        }   
    }

    // Returns the current thread's unique ID, assigning it if necessary
    public static int getCurrentThreadUniqueId() {      
        return threadId.get();
    }   
}

【问题讨论】:

    标签: java multithreading concurrency synchronization semaphore


    【解决方案1】:

    Java 5 引入了explicit locks,它有一个tryLock 操作。所以使用显式锁而不是同步块,那么你可以调用tryLock

    private Lock lock = ...;
    
    private boolean acquireLock() {
      if (lock.tryLock()) {
          ...
          return true;
      } else {
          return false;
      }
    }
    

    【讨论】:

      【解决方案2】:

      使用SemaphoretryAcquire 方法。

      【讨论】:

        【解决方案3】:

        您应该尝试使用 ReentrantLock 类。 您可以尝试使用 tryLock() 或直接调用 lock() 来获取。确保你学习了 ReentrantLock 的 API。 这是一个简单的例子:

            Lock lock = new ReentrantLock();
            // block until lock is acquired. Make sure to call unlock in a finally
            // statement!
            try {
                lock.lock();
            } finally {
                lock.unlock();
            }
            // or try to gain the lock
            boolean success = lock.tryLock();
            if(success) {
                //some logic..
            }
        
            //or try to gain lock within time frame
            try {
                lock.tryLock(1, TimeUnit.SECONDS);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        

        【讨论】:

          【解决方案4】:

          其他人指出,有一个优秀的库可以完成所有这些以及更多工作,但如果您对实现这类事情感兴趣,那么您需要支持比较和设置操作的东西。幸运的是,从 Java5 开始有了 java.util.concurrent.atomic.Atomic* 类!

          public class MyLock {
            private final AtomicBoolean locked = new AtomicBoolean();
          
            public boolean tryLock() {
              if (locked.get()) return true;
              return locked.compareAndSet(false, true);
            }
          
            public boolean unlock() {
              if (l!ocked.get()) return true;
              return locked.compareAndSet(true, false);
            }
          }
          

          以上是一个非常幼稚的实现,但它展示了如何实现这是一种无锁方式的基础知识。

          有关并发数据结构的实现细节的更多信息,请参阅 Shavit 和 Herlihy 的精彩“多处理器编程的艺术”,当然还有 Goetz 等人的基本“Java 并发实践”。

          【讨论】:

            【解决方案5】:

            是的,您遗漏了一些东西(您的解决方案不起作用)

            假设有 2 个线程同时到达 if() 语句,并通过它。 都将 lockAvailable 设置为 false 并返回 true

            如果您不想违反临界区规则或使用其他方法,则必须使函数同步。

            【讨论】:

            • 在函数上同步会导致一个线程等待,这正是原始发布者想要避免的。
            • 我并不是说这是他想要的,但它会破坏临界区规则,除非他同步它。无论如何,等待只会锁定而不是整个关键部分,这不是主要的性能下降
            猜你喜欢
            • 2020-01-29
            • 2022-01-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-10-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多