顺序打印123
方法1:CountDownLatch
static class _1st {
public static void main(String[] args) throws InterruptedException {
_1st handler = new _1st();
for (int i = 0; i < 50; i++) {
CountDownLatch countDownLatch = new CountDownLatch(3);
run(countDownLatch);
countDownLatch.await();
System.out.println();
}
}
private static void run(CountDownLatch countDownLatch) {
Foo foo = new Foo();
Thread t1 = new Thread(() -> {
try {
foo.first(() -> {
System.out.print("1");
countDownLatch.countDown();
});
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
foo.second(() -> {
System.out.print("2");
countDownLatch.countDown();
});
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t3 = new Thread(() -> {
try {
foo.third(() -> {
System.out.print("3");
countDownLatch.countDown();
});
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t3.start();
t2.start();
t1.start();
}
static class Foo {
CountDownLatch latch12 = new CountDownLatch(1);
CountDownLatch latch23 = new CountDownLatch(1);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
latch12.countDown();
}
public void second(Runnable printSecond) throws InterruptedException {
latch12.await();
printSecond.run();
latch23.countDown();
}
public void third(Runnable printThird) throws InterruptedException {
latch23.await();
printThird.run();
}
}
}
方法2:LockSupport
static class _3rd {
public static void main(String[] args) throws InterruptedException {
FIFOMutex mutex = new FIFOMutex();
MyThread t1 = new MyThread("1", mutex);
MyThread t2 = new MyThread("2", mutex);
MyThread t3 = new MyThread("3", mutex);
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
assert MyThread.count == 50;
System.out.print("Finished");
}
public static class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
public void lock() {
Thread current = Thread.currentThread();
waiters.add(current);
while (waiters.peek() != current || !locked.compareAndSet(false, true)) {
LockSupport.park(this);
}
waiters.remove();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
static class MyThread extends Thread {
private String name;
private FIFOMutex mutex;
public static int count;
public MyThread(String name, FIFOMutex mutex) {
this.name = name;
this.mutex = mutex;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
mutex.lock();
count++;
System.out.println(name);
mutex.unlock();
}
}
}
}
方法3:Thread
static class _4th {
public static void main(String[] args) throws InterruptedException {
_4th handler = new _4th();
for (int i = 0; i < 50; i++) {
Thread t1 = new Thread(() -> System.out.printf("%d", 1));
t1.start();
t1.join();
Thread t2 = new Thread(() -> System.out.printf("%d", 2));
t2.start();
t2.join();
Thread t3 = new Thread(() -> System.out.printf("%d", 3));
t3.start();
t3.join();
System.out.println();
}
}
}
方法4:synchronized
static class _4th_1 {
private static Integer num = 1;
static class Thread1 implements Runnable {
@Override
public void run() {
while (true) {
synchronized (this) {
if (num % 3 == 1) {
System.out.println("Thread1: 1");
num++;
if (num > 49) break;
}
}
}
}
}
static class Thread2 implements Runnable {
@Override
public void run() {
while (true) {
synchronized (this) {
if (num % 3 == 2) {
System.out.println("Thread2: 2");
num++;
if (num > 50) break;
}
}
}
}
}
static class Thread3 implements Runnable {
@Override
public void run() {
while (true) {
synchronized (this) {
if (num % 3 == 0) {
System.out.println("Thread3: 3");
num++;
if (num > 50) break;
}
}
}
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(new Thread1());
Thread thread2 = new Thread(new Thread2());
Thread thread3 = new Thread(new Thread3());
thread1.start();
thread2.start();
thread3.start();
}
}
方法5:synchronized+struct
- 实现的目标是
Thread1->Thread2->Thread3-Thread1
如此往复 - 每个线程必须同事持有两个对象锁,才能进行打印操作
- 一个对象锁是
prev
(前一个线程对应的对象锁),要保证当前线程在前一个线程操作完(prev
前一个线程释放了对象锁)才开始执行 - 另外一个锁是自身线程的对象锁
cur
,为了控制执行的顺序
notify()
是唤醒下一个等待的线程wait()
是立即释放当前调用的线程的对象锁,当前线程进入休眠状态,等待其他线程的notify()
来唤醒
static class _4th_2 {
public static void main(String[] args) throws InterruptedException {
Object _1 = new Object();
Object _2 = new Object();
Object _3 = new Object();
MyThread t1 = new MyThread("1", _3, _1);
MyThread t2 = new MyThread("2", _1, _2);
MyThread t3 = new MyThread("3", _2, _3);
new Thread(t1).start();
Thread.sleep(100);
new Thread(t2).start();
Thread.sleep(100);
new Thread(t3).start();
Thread.sleep(100);
}
static class MyThread implements Runnable {
private String name;
private Object prev;
private Object cur;
public MyThread(String name, Object prev, Object cur) {
this.name = name;
this.prev = prev;
this.cur = cur;
}
@Override
public void run() {
int cnt = 50;
while (cnt > 0) {
synchronized (prev) {
synchronized (cur) {
System.out.printf("%s", name);
cnt--;
cur.notifyAll();
}
try {
if (cnt == 0) {
prev.notifyAll();
} else {
prev.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
- 总结
wait()
与 notify/notifyAll()
是Object
类的方法,在执行两个方法时,要先获得锁。- 当线程执行
wait()
时,会把当前的锁释放,然后让出CPU
,进入等待状态。 - 当执行
notify/notifyAll
方法时,会唤醒一个处于等待该 对象锁 的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchroniz ed
修饰的代码块)后再释放锁。 - 从这里可以看出,
notify/notifyAll()
执行后,并不立即释放锁,而是要等到执行完临界区中代码后,再释放。所以在实际编程中,我们应该尽量在线程调用notify/notifyAll()
后,立即退出临界区。即不要在notify/notifyAll()
后面再写一些耗时的代码
方法6:ReentrantLock
ReentrantLock
是可重入锁,它持有一个锁计数器
static class _4th_3 {
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
new Thread(new Thread3()).start();
}
private static ReentrantLock lock = new ReentrantLock();
private static int signal = 0;
static class Thread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; ) {
try {
lock.lock();
while (signal % 3 == 0) {
System.out.printf("%s", "1");
signal++;
i++;
}
} finally {
lock.unlock();
}
}
}
}
static class Thread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; ) {
try {
lock.lock();
while (signal % 3 == 1) {
System.out.printf("%s", "2");
signal++;
i++;
}
} finally {
lock.unlock();
}
}
}
}
static class Thread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; ) {
try {
lock.lock();
while (signal % 3 == 2) {
System.out.printf("%s", "3");
signal++;
i++;
}
} finally {
lock.unlock();
}
}
}
}
}
方法7:ReentrantLock+Condition
static class _4th_5 {
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
new Thread(new Thread3()).start();
}
private static ReentrantLock lock = new ReentrantLock();
private static int signal = 0;
private static Condition c1 = lock.newCondition();
private static Condition c2 = lock.newCondition();
private static Condition c3 = lock.newCondition();
static class Thread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
lock.lock();
while (signal % 3 != 0) {
c1.await();
}
System.out.printf("%s", "1");
signal++;
c2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
static class Thread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
lock.lock();
while (signal % 3 != 1) {
c2.await();
}
System.out.printf("%s", "2");
signal++;
c3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
static class Thread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
lock.lock();
while (signal % 3 != 2) {
c3.await();
}
System.out.printf("%s", "3");
signal++;
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
}
方法8:Semaphore
Semaphore
是用来保护一个或者多个共享资源的访问,Semaphore
内部维护了一个计数器,其值为可以访问的共享资源的个数。一个线程要访问共享资源,先获得信号量,如果信号量的计数器值大于1,意味着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。如果计数器值为0
,线程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量
static class _4th_4 {
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
new Thread3().start();
}
private static Semaphore s1 = new Semaphore(1);
private static Semaphore s2 = new Semaphore(0);
private static Semaphore s3 = new Semaphore(0);
static class Thread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
s1.acquire();
System.out.printf("%s", "1");
s2.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Thread2 extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
s2.acquire();
System.out.printf("%s", "2");
s3.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Thread3 extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
s3.acquire();
System.out.printf("%s", "3");
s1.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}