两个线程交替打印0, 1,2, 3……
class Worker implements Runnable {
Semaphore semaphore;
volatile static int n = 0;
public Worker(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
while (true) {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "\t" + n++);
Thread.sleep(1000);
semaphore.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这是段错误代码,因为可能一个线程可能会连续抢到信号量。
class Worker implements Runnable {
Semaphore semaphore;
volatile static int n = 0;
final int m;
public Worker(Semaphore semaphore, int m) {
this.semaphore = semaphore;
this.m = m;
}
@Override
public void run() {
try {
while (true) {
semaphore.acquire();
if ((n & 0x1) == m) {
System.out.println(Thread.currentThread().getName() + "\t" + n++);
Thread.sleep(1000);
}
semaphore.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试代码:
final Semaphore semaphore = new Semaphore(1, true);
Worker worker1 = new Worker(semaphore, 0);
Worker worker2 = new Worker(semaphore, 1);
Thread thread1 = new Thread(worker1);
Thread thread2 = new Thread(worker2);
thread1.start();
thread2.start();
注意,这里使用了是公平锁,减少了一个线程连续抢占信号量的可能。
此外,int会溢出,最大值 + 1会得到最小值,从奇数变成偶数,并不影响移位运算。
还有一点需要注意,不能%2,因为可能得到负数。
除此之外呢,还有没有其他的方案呢?
class Worker implements Runnable {
Lock lock;
Condition[] conditionArray;
volatile static int n = 0;
final int m;
public Worker(Lock lock, Condition[] conditionArray, int m) {
this.lock = lock;
this.m = m;
this.conditionArray = conditionArray;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
while ((n & 0x1) != m) {
System.out.println(Thread.currentThread().getName() + "等待" );
conditionArray[m].await();
}
System.out.println(Thread.currentThread().getName() + "\t" + n++);
Thread.sleep(1000);
conditionArray[1 - m].signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
测试代码:
Lock lock = new ReentrantLock();
Condition[] array = new Condition[2];
for (int i = 0; i < array.length; i++) {
array[i] = lock.newCondition();
}
final Semaphore semaphore = new Semaphore(1);
Worker worker1 = new Worker(lock, array, 0);
Worker worker2 = new Worker(lock, array, 1);
Thread thread1 = new Thread(worker1);
Thread thread2 = new Thread(worker2);
thread1.start();
thread2.start();
其实只有一个Condition也可以。还有没有其他方案呢?
class Worker implements Runnable {
volatile static AtomicBoolean atomicBoolean = new AtomicBoolean();
volatile static int n = 0;
boolean flag;
public Worker(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
while (true) {
while (atomicBoolean.get() != flag) {
//Thread.sleep(500);
}
System.out.println(Thread.currentThread().getName() + "\t" + n++);
atomicBoolean.set(!flag);
}
}
}
这样没有用锁,可以实现,不过美中不足是,只能通过sleep等待。
2019-07-18
方案二,只需要一个Condition就可以了。
最后一个方案不可取,因为处于无锁状态下,线程的调度不能预测。如果去掉Sleep,这种现象会比较明显的观测出来。
关于Condition的代码有bug,正确姿势请参考:Condition