Java 与线程同步
我有一个两部分的问题Java 与线程同步,java,synchronization,synchronized,synchronized-block,Java,Synchronization,Synchronized,Synchronized Block,我有一个两部分的问题 我有一个类,其中有一个函数,在给定的时间内只能由任何一个线程访问。将此设置为同步函数或同步块仍然允许多个线程,因为类中有不同的线程访问它。如何确保只有一个线程访问此代码?(参见下面的代码示例) 使用synchronized函数,对该函数的调用将排队。有没有办法只允许对函数的最后一次调用访问代码?因此,如果我让Thread1当前正在访问我的函数,那么Thread2和Thread3尝试访问它(按顺序),Thread1完成后,只有Thread3可以访问 public void d
同步
函数或同步
块仍然允许多个线程,因为类中有不同的线程访问它。如何确保只有一个线程访问此代码?(参见下面的代码示例)public void doATask() {
// I create a new thread so the interface is not blocked
new Thread(new Runnable() {
@Override
public void run() {
doBackgroundTask();
}
}).start();
}
private void doBackgroundTask(MyObject obj) {
// perform long task here that is only being run by one thread
// and also only accepts the last queued thread
}
谢谢你的帮助 如果示例中的第二个线程只能
返回
,则可以使用锁和跟踪执行该方法的最后一个线程的组合。它可能是这样的:
private volatile Thread lastThread;
private final ReentrantLock lock = new ReentrantLock();
private void doBackgroundTask(Object obj) throws InterruptedException {
Thread currentThread = Thread.currentThread();
lastThread = currentThread;
try {
// wait until lock available
lock.lockInterruptibly();
// if a thread has arrived in the meantime, exit and release the lock
if (lastThread != currentThread) return;
// otherwise
// perform long task here that is only being run by one thread
// and also only accepts the last queued thread
} finally {
lock.unlock();
}
}
带有额外日志记录的完整工作测试,该日志记录显示线程交错,并且T2在不执行任何操作的情况下退出:
class Test {
private volatile Thread lastThread;
private final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws Exception {
final Test instance = new Test();
Runnable r = new Runnable() {
@Override
public void run() {
try {
instance.doBackgroundTask(null);
} catch (InterruptedException ignore) {}
}
};
Thread t1 = new Thread(r, "T1");
Thread t2 = new Thread(r, "T2");
Thread t3 = new Thread(r, "T3");
t1.start();
Thread.sleep(100);
t2.start();
Thread.sleep(100);
t3.start();
}
private void doBackgroundTask(Object obj) throws InterruptedException {
Thread currentThread = Thread.currentThread();
System.out.println("[" + currentThread.getName() + "] entering");
lastThread = currentThread;
try {
// wait until lock available
lock.lockInterruptibly();
// if a thread has arrived in the meantime, exit and release the lock
if (lastThread != currentThread) return;
// otherwise
// perform long task here that is only being run by one thread
// and also only accepts the last queued thread
System.out.println("[" + currentThread.getName() + "] Thinking deeply");
Thread.sleep(1000);
System.out.println("[" + currentThread.getName() + "] I'm done");
} finally {
lock.unlock();
System.out.println("[" + currentThread.getName() + "] exiting");
}
}
}
输出:
[T1]进入
[T1]深思熟虑
[T2]进入
[T3]进入
[T1]我做完了
[T1]退出
[T2]退出
[T3]深思熟虑
[T3]我做完了
[T3]退出
如果示例中的第二个线程只能返回
,则可以使用锁和跟踪执行该方法的最后一个线程的组合。它可能是这样的:
private volatile Thread lastThread;
private final ReentrantLock lock = new ReentrantLock();
private void doBackgroundTask(Object obj) throws InterruptedException {
Thread currentThread = Thread.currentThread();
lastThread = currentThread;
try {
// wait until lock available
lock.lockInterruptibly();
// if a thread has arrived in the meantime, exit and release the lock
if (lastThread != currentThread) return;
// otherwise
// perform long task here that is only being run by one thread
// and also only accepts the last queued thread
} finally {
lock.unlock();
}
}
带有额外日志记录的完整工作测试,该日志记录显示线程交错,并且T2在不执行任何操作的情况下退出:
class Test {
private volatile Thread lastThread;
private final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws Exception {
final Test instance = new Test();
Runnable r = new Runnable() {
@Override
public void run() {
try {
instance.doBackgroundTask(null);
} catch (InterruptedException ignore) {}
}
};
Thread t1 = new Thread(r, "T1");
Thread t2 = new Thread(r, "T2");
Thread t3 = new Thread(r, "T3");
t1.start();
Thread.sleep(100);
t2.start();
Thread.sleep(100);
t3.start();
}
private void doBackgroundTask(Object obj) throws InterruptedException {
Thread currentThread = Thread.currentThread();
System.out.println("[" + currentThread.getName() + "] entering");
lastThread = currentThread;
try {
// wait until lock available
lock.lockInterruptibly();
// if a thread has arrived in the meantime, exit and release the lock
if (lastThread != currentThread) return;
// otherwise
// perform long task here that is only being run by one thread
// and also only accepts the last queued thread
System.out.println("[" + currentThread.getName() + "] Thinking deeply");
Thread.sleep(1000);
System.out.println("[" + currentThread.getName() + "] I'm done");
} finally {
lock.unlock();
System.out.println("[" + currentThread.getName() + "] exiting");
}
}
}
输出:
[T1]进入
[T1]深思熟虑
[T2]进入
[T3]进入
[T1]我做完了
[T1]退出
[T2]退出
[T3]深思熟虑
[T3]我做完了
[T3]退出
您需要的可能是一个工作线程,它等待信号来执行某些工作doATask()
只发送一个信号来触发工作。累计信号相当于一个信号
final Object lock = new Object();
MyObject param = null;
public void doATask(arg)
synchronized(lock)
param=arg;
lock.notify();
MyObject awaitTask()
synchronized(lock)
while(param==null)
lock.wait();
tmp=param;
param=null;
return tmp;
// worker thread
public void run()
while(true)
arg = awaitTask();
doBackgroundTask(arg);
您需要的可能是一个工作线程,它等待信号来执行某些工作
doATask()
只发送一个信号来触发工作。累计信号相当于一个信号
final Object lock = new Object();
MyObject param = null;
public void doATask(arg)
synchronized(lock)
param=arg;
lock.notify();
MyObject awaitTask()
synchronized(lock)
while(param==null)
lock.wait();
tmp=param;
param=null;
return tmp;
// worker thread
public void run()
while(true)
arg = awaitTask();
doBackgroundTask(arg);
被取消的线程应该做什么?他们应该被终止还是返回哨兵或抛出异常?被取消的线程应该在另一个线程尝试调用该方法时立即取消,还是在线程1完成时取消?这里有太多的歧义,你想做什么?线程2会发生什么?它应该在线程3到达时退出吗?抛出一些异常?第二个线程应该被终止。它不需要抛出异常。可以忽略它。被取消的线程可以在任何时候被取消……只要另一个线程尝试调用该方法,或者当Thread1完成时。@Nick:如果是这种情况,那么您可能不想使用线程,而可能需要某种事件驱动的系统。嗯,函数doATask()是由用户按下按钮引起的。在这种情况下,用户正在扫描一堆对象,因此他们正在快速点击按钮。被取消的线程应该做什么?他们应该被终止还是返回哨兵或抛出异常?被取消的线程应该在另一个线程尝试调用该方法时立即取消,还是在线程1完成时取消?这里有太多的歧义,你想做什么?线程2会发生什么?它应该在线程3到达时退出吗?抛出一些异常?第二个线程应该被终止。它不需要抛出异常。可以忽略它。被取消的线程可以在任何时候被取消……只要另一个线程尝试调用该方法,或者当Thread1完成时。@Nick:如果是这种情况,那么您可能不想使用线程,而可能需要某种事件驱动的系统。嗯,函数doATask()是由用户按下按钮引起的。在这种情况下,用户正在扫描一堆对象,因此他们正在快速点击按钮。这确实是另一种看待它的方式-在这种情况下,您可以简单地使用阻塞队列或类似的机制,而不是等待/通知。是的,但队列只需要保留最后一项。不确定哪个队列实现了这一点。这确实是另一种看待它的方式——在这种情况下,您可以简单地使用阻塞队列或类似的机制,而不是等待/通知。是的,但是队列只需要保留最后一项。不确定哪个队列实现了这个。