1 Поток:
public class First implements Runnable {
private Semaphore semaphore;
public First(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run(){
for (int i = 0; i < 5; i++) {
semaphore.enter();
System.out.println(1);
semaphore.leave();
}
}
}
2 поток:
public class Second implements Runnable {
private Semaphore semaphore;
public SequentalCleaner(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run(){
for (int i = 0; i < 5; i++) {
semaphore.enter();
System.out.println(2);
semaphore.leave();
}
}
}
Семафор:
public class Semaphore {
private int cur;
public Semaphore(int cur) {
this.cur = cur;
}
public void enter() {
while(cur == 0){
}
--cur;
}
public void leave(){
++cur;
}
}
Это не синхронизировано. Как сделать синхронизированным? Вывод при запуске потоков:
2 1 2 1 2 1 1 1
или, например
1 1 1 1 1 2 2 2 2 2
Т.е. они даже не заканчивают свою работу.
Хотя конечно варианты реализации семафоров лучше в книгах по многопоточности посмотреть, вот вариант "велосипеда":
public class TestSemaphor {
private final static int NThreads = 60;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
Thread[] threads = new Thread[NThreads];
for (int i = 0; i < NThreads; i++) {
threads[i] = new Thread(new Runner(semaphore, i));
}
for (int i = 0; i < NThreads; i++) {
threads[i].start();
}
}
}
class Runner implements Runnable {
private Semaphore semaphore;
private int label;
public Runner(Semaphore semaphore, int label) {
this.semaphore = semaphore;
this.label = label;
}
@Override
public void run() {
int r = 0;
System.out.println("thread " + label + " start");
semaphore.enter();
System.out.println("thread " + label + " is working");
for (int k = 0; k < 10000; k++) {
r = new Random().nextInt();
}
;
System.out.println("thread " + label + " has ended work");
semaphore.leave();
System.out.println("thread " + label + " end " + r);
}
}
class Semaphore {
private int curMax;
private int cur;
private Object lock = new Object();
public Semaphore(int curMax) {
this.curMax = curMax;
}
public void enter() {
synchronized (lock) {
cur++;
if (cur > curMax) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public void leave() {
synchronized (lock) {
cur--;
lock.notify();
}
}
}
Собственно идея такая: синхронизации используется только для того, чтобы обновить счетчик очереди. Если при этом у нас потоков больше чем надо, то отправляем текущий поток в ожидание. При освобождении семафора вызываем на случайном ожидающем потоке notify. Обратите внимание что синхронизация идет по одному и тому же объекту - поэтому happens before присутствует
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости