简单的Java代码允许单线程访问和其他线程跳过/继续

时间:2011-09-09 08:30:02

标签: java multithreading concurrency synchronization semaphore

换句话说,如果线程无法访问锁定,我不希望线程等待(如在同步中),我想线程执行只是在它无法获得锁定时立即返回。

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

private static boolean lockAvailable = true;

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

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

编辑:

感谢您指出Semaphore(!)

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

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

5 个答案:

答案 0 :(得分:8)

Java 5引入了explicit locks操作tryLock。因此,使用显式锁而不是同步块,然后您可以调用tryLock

private Lock lock = ...;

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

答案 1 :(得分:3)

使用SemaphoretryAcquire方法。

答案 2 :(得分:0)

您应该尝试使用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();
    }

答案 3 :(得分:0)

其他人已经指出,有很好的库可以完成所有这些以及更多,但是如果你对实现这类事情感兴趣,那么你需要一些支持比较和设置操作的东西。幸运的是,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 Concurrency in Practice”。

答案 4 :(得分:-3)

是的,你错过了什么(你的解决方案不起作用)

想象两个线程同时到达if()语句,然后传递它。 都将lockAvailable设置为false并返回true

如果您不想违反关键部分规则或使用其他方法,则必须使该功能同步。