Я экспериментирую с парковкой потоков и решил создать какой-то сервис. Вот как это выглядит:
public class TestService {
private static final Logger logger = LoggerFactory.getLogger(TestService.class); // logback I think this logger causes some troubles
private final CountDownLatch stopLatch;
private final Object parkBlocker = new Object();
private volatile boolean stopped;
private final Thread[] workers;
public TestService(int parallelizm) {
stopLatch = new CountDownLatch(parallelizm);
workers = new Thread[parallelizm];
for (int i = 0; i < parallelizm; i++) {
workers[i] = new Thread(() -> {
try {
while (!stopped) {
logger.debug("Parking " + Thread.currentThread().getName());
LockSupport.park(parkBlocker);
logger.debug(Thread.currentThread().getName() + " unparked");
}
} finally {
stopLatch.countDown();
}
});
}
}
public void start() {
Arrays.stream(workers).forEach(t -> {
t.start();
logger.debug(t.getName() + " started");
});
}
public boolean stop(long timeout, TimeUnit unit) throws InterruptedException {
boolean stoppedSuccefully = false;
this.stopped = true;
unparkWorkers();
if (stopLatch.await(timeout, unit)) {
stoppedSuccefully = true;
}
return stoppedSuccefully;
}
private void unparkWorkers() {
Arrays.stream(workers).forEach(w -> {
LockSupport.unpark(w);
logger.debug("Un-park call is done on " + w.getName());
});
}
}
Проблема, с которой я столкнулся, заключалась в том, что если я затем протестирую эту службу следующим образом:
public static void main(String[] args) = {
while(true) {
TestService service = new TestService(2);
service.start();
if (!service.stop(10000, TimeUnit.MILLISECONDS))
throw new RuntimeException();
}
}
Иногда у меня было следующее поведение:
14:58:55.226 [main] DEBUG com.pack.age.TestService - Thread-648 started
14:58:55.227 [Thread-648] DEBUG com.pack.age.TestService - Parking Thread-648
14:58:55.227 [main] DEBUG com.pack.age.TestService - Thread-649 started
14:58:55.227 [main] DEBUG com.pack.age.TestService - Un-park call is done on Thread-648
14:58:55.227 [Thread-648] DEBUG com.pack.age.TestService - Thread-648 unparked
14:58:55.227 [main] DEBUG com.pack.age.TestService - Un-park call is done on Thread-649
14:58:55.227 [Thread-649] DEBUG com.pack.age.TestService - Parking Thread-649
Exception in thread "main" java.lang.RuntimeException
at com.pack.age.Test$.main(Test.scala:12)
at com.pack.age.Test.main(Test.scala)
Тема висит на парковке:
"Thread-649" #659 prio=5 os_prio=0 tid=0x00007efe4433f000 nid=0x7691 waiting on condition [0x00007efe211c8000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000720739a68> (a java.lang.Object)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at com.pack.age.TestService.lambda$new$0(TestService.java:27)
at com.pack.age.TestService$$Lambda$1/1327763628.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Я не вижу никакой гонки в парковке-разпарковке в сервисе. Более того, если unpark
вызывается перед park
, park
гарантированно не блокируется (это то, что говорят javadocs).
Может быть, я неправильно использую LockSupport::park
. Можете ли вы предложить какое-либо исправление?
java.util.logging
(здесь нет log4j). Может быть, это действительно проблема (хотя я не понимаю, как). - person daniu   schedule 16.05.2018System.out.println
все сообщения, и код работал нормально. После перехода на log4j/logback я начал решать эту проблему. - person St.Antario   schedule 16.05.2018ReentrantLock
для синхронизации, которая, в свою очередь, использует поток парковки для ожидания (черезAbstractQueuedSynchronizer
). Таким образом,LockSupport::park
кажется небезопасным для использования с каким-то чужеродным кодом, который может сам парковать / разблокировать потоки. - person St.Antario   schedule 16.05.2018ReentrantLock
, это проблема с этим, а не с логбэком (который только вводит проблему в вашем случае) - так что это реальная проблема JDK. В конце концов, вы можете использоватьReentrantLock
в других (не чужих) частях кода и вызывать такое же поведение. - person daniu   schedule 16.05.2018unpark
не принимает объектblocker
. Просто нить. - person St.Antario   schedule 16.05.2018unpark()
использовать блокирующий объект, если используете тот же самый (что и делаете вы). Но в целом я согласен с тем, чтоunpark()
принимаетThread
, тем более что предпочтительная обработка параллелизма в Java используетExecutorService
s и больше не предполагает прямого использования самогоThread
. Это, вероятно, также способ решить вашу проблему. - person daniu   schedule 16.05.2018ReentrantLock
, поэтому разрешение теряется. И в следующий раз, когда мы припаркуем поток, он будет припаркован (разрешение было потеряно вReentrantLock
). Это правильно? - person St.Antario   schedule 16.05.2018lock.lock(); long l = 0; while(l++ < 100000000L){} lock.unlock();
- person St.Antario   schedule 16.05.2018LockSupport
javadoc указываются базовые примитивы блокировки потоков для создания блокировок и других классов синхронизации. Я предполагаю, что урок заключается в том, чтобы не использовать его вместе с блокировками (а лучше оставаться на том же уровне синхронизации). - person daniu   schedule 16.05.2018