После игры с вашим кодом в течение некоторого времени моя последняя версия, приведенная ниже, не совсем то, что вы просили, но, на мой взгляд, она очень близка.
Java 5 представил много утилиты параллелизма , в том числе блокировка объектов . (Обратитесь к Параллельному уроку Основы trail, который является частью Oracle java учебников.) Еще одна ссылка, которая помогла мне понять утилиты java для параллелизма, была книга Размышления в Java (4-е издание) Брюса Эккеля.
Поэтому я использовал класс ReentrantLock вместо синхронизации. Я также синхронизировал все методы в вашем классе Printer
, так как они вызываются из нескольких потоков. Вот моя версия этого класса. Я добавил идентификатор потока в метод print()
, чтобы вы могли соотнести напечатанный вывод с вызывающим потоком.
public class Printer {
String prefix;
int count;
public synchronized void setPrefix(String prefix) {
this.prefix = prefix;
}
public synchronized void setCount(int count) {
this.count = count;
}
public synchronized void print() {
System.out.printf("[%d] %s: %3d%n", Thread.currentThread().getId(), prefix, count);
}
}
Вот моя версия вашего Counter
класса.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
public class Counter implements Runnable {
private static ReentrantLock theLock = new ReentrantLock();
private int start = 0;
private int finish = 0;
private boolean reverse = false;
private Printer printer;
private String prefix;
public Counter(String prefix, int start, int finish, boolean reverse, Printer printer)
throws Exception {
if (finish < start && printer == null) {
throw new Exception("finish must be equal or greater than start, no nulls allowed.");
}
else {
this.start = start;
this.finish = finish;
this.reverse = reverse;
this.printer = printer;
this.prefix = prefix;
}
}
@Override
public void run() {
if (reverse) {
for (int i = start; i <= finish; i++) {
try {
Thread.sleep(100);
theLock.lock();
printer.setPrefix(prefix);
printer.setCount(i);
printer.print();
}
catch (Exception e) {
System.err.println("Already interrupted.");
}
finally {
if (theLock.isHeldByCurrentThread()) {
theLock.unlock();
}
}
}
}
else {
for (int i = finish; i >= start; i--) {
try {
Thread.sleep(100);
theLock.lock();
printer.setPrefix(prefix);
printer.setCount(i);
printer.print();
}
catch (Exception e) {
System.err.println("Already interrupted.");
}
finally {
if (theLock.isHeldByCurrentThread()) {
theLock.unlock();
}
}
}
}
}
public static void main(String[] args) throws Exception {
Printer printer = new Printer();
ExecutorService es = Executors.newFixedThreadPool(2);
es.execute(new Counter("Prefix1", 0, 100, false, printer));
es.execute(new Counter("Prefix2", 0, 100, true, printer));
es.shutdown();
}
}
Насколько я могу судить, вы не можете полностью контролировать планирование потоков в java, следовательно, если вы запустите приведенный выше код, который вы можете увидеть в выходных строках, например:
[14] Prefix2: 0
[13] Prefix1: 100
[13] Prefix1: 99
[14] Prefix2: 1