如果我有一个synchronized方法并且两个线程正在等待输入它们,则它们似乎进入Last In First Executed线程。有没有办法让这首先被执行?
这是我正在使用的单元测试:
package com.test.thread;
import org.apache.log4j.Logger;
import org.junit.Test;
public class ThreadTest {
private static final Logger log = Logger.getLogger(ThreadTest.class);
@Test
public void testThreading() throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() { synchd("1"); }
});
Thread t2 = new Thread(new Runnable() {
public void run() { synchd("2"); }
});
Thread t3 = new Thread(new Runnable() {
public void run() { synchd("3"); }
});
t3.start();
Thread.sleep(5);
t1.start();
t2.start();
Thread.sleep(12000);
}
public static synchronized void synchd(String output) {
log.debug(output);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// do nothing
}
}
}
此输出始终为3,2,1,我想找到一种方法,使其为3,1,2。
答案 0 :(得分:8)
不,他们没有按照他们的到达顺序执行。
执行顺序取决于与线程调度相关的大量参数,并且通常是不可预测的。
答案 1 :(得分:4)
使用java.util.concurrent.locks.ReentrantLock
,您可以指定与此相关的公平性政策。将true
传递给引用的构造函数会请求锁定“公平”。这意味着在文档中有点模糊,但研究the underlying AbstractQueuedSynchronizer
type的文档和ReentrantLock$FairSync
的实现(在Sun JDK中)提供了额外的提示:
即,当一个线程试图以 fair 模式获取锁定时,它不会在其他等待线程之前“插入”; 如果有任何其他线程正在等待锁定,新到达的线程将“获取它”。
现在,它仍然可能 - 虽然极不可能 - 这个新排队的线程将在其当前持有者下次释放时获取下一个锁,如果它当时恰好结束由于前辈被中断而排队,但观察到即使条件队列的逻辑模型是等待线程的 set ,在上述实现中它实际上是队列( CLH Queue ,在论文The java.util.concurrent Synchronizer Framework中的Java库的应用程序中描述)。在 fair 模式下,只有队列中的第一项才能获得锁定。
显然,两个线程在尝试“同时”获取锁定时可能会竞争。如果有一个公平的锁定,你可以预期如果线程 A 首先到达并调用Lock#lock()
,并且最终必须等待,因为锁是由线程 C 保持的,后来线程 B 到达并调用Lock#lock()
,而线程 C 仍然保留它, B 将在后面 已经排队的 A ,一旦 C 释放锁定, A 将在 B 之前获得机会获得它。请参阅AbstractQueuedSynchronizer$unparkSuccessor()
中的实现,了解从CLH队列的头部向其尾部的特定前进。 A 比 B 更靠近头部。
ReentrantLock
的文档警告说,即使在 fair 模式下运行,已经等待锁的线程也可能会丢失到当前持有锁的线程释放它并获取它再次。我认为 - 但我不确定 - 当当前线程在尚未登陆队列的其他线程之前获胜时会发生这种情况。另请注意有关ReentrantLock#tryLock()
的警告;与定时ReentrantLock#tryLock(long, TimeUnit)
不同,前者不尊重公平政策。
该调查基于一种实施方式得出一些结论。一般来说,采用Mr. Barousse's view会更安全:收购顺序最好被认为是来自一组服务员的随机抓取。但是,如果对公平政策进行深入研究,你会发现存在一些决定论。但它不是免费的;注意禁止在驳船时减少吞吐量的警告。