1. 程式人生 > 其它 >多執行緒 -- 交替列印

多執行緒 -- 交替列印

輸入一個數字n,交替列印foobar n 次

public static void main(String[] args) {
    int n = 10; //列印次數
    FooBar fooBar = new FooBar(n);
    Runnable printFoo = () -> {
        System.out.print("foo");
    };
    Runnable printBar = () -> {
        System.out.print("bar");
    };
    Thread fooThread = new Thread(() -> {
        try {
            fooBar.foo(printFoo);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    Thread barThread = new Thread(() -> {
        try {
            fooBar.bar(printBar);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    fooThread.start();
    barThread.start();
}

1. ReentrantLock

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class FooBar {
	private int n;
	private volatile boolean bl;
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();

	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			lock.lock();
			try {
				while (bl) { //當false時輸出foo,否則在這裡阻塞
					condition.await();
				}
				// printFoo.run() outputs "foo".
				printFoo.run();
				bl = true;
				condition.signal();
			} finally {
				lock.unlock();
			}

		}
	}

	public void bar(Runnable printBar) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			lock.lock();
			try {
				while (!bl) { //當true時輸出bar,否則在這裡阻塞
					condition.await();
				}
				// printBar.run() outputs "bar".
				printBar.run();
				bl = false;
				condition.signal();
			} finally {
				lock.unlock();
			}
		}
	}
}

2. 使用訊號量Semaphore

class FooBar {
	private int n;
	
	private Semaphore fooSemaphore = new Semaphore(1); //控制輸出foo
	private Semaphore barSemaphore = new Semaphore(0); //控制輸出bar

	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			fooSemaphore.acquire(); //可以認為需要得到這個訊號量才能進行下一步,不然就阻塞
			printFoo.run();
			barSemaphore.release(); //將訊號量+1
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			barSemaphore.acquire();
			printBar.run();
			fooSemaphore.release();
		}
	}
}

3. 使用CyclicBarrier (與CountDownLatch 的主要區別是CyclicBarrier 可以重複使用)

class FooBar {
	private int n;
	private volatile boolean bl;

	private CyclicBarrier cb = new CyclicBarrier(2);

	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			while(bl) { //當是true時, 進行空轉或者讓出, false時列印foo
                Thread.yield();
            }
            printFoo.run();
			bl = true;
			try {
				cb.await(); //將foo執行緒進行同步,相當於-1,然後阻塞當前執行緒
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			try {
				cb.await(); //將bar執行緒同步
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
			printBar.run();
			bl = false;
		}
	}
}

4. 用Thread.yield(),讓出CPU,具體還是得看執行緒的排程

class FooBar {
	private int n;
	private volatile boolean bl;

	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {
		for (int i = 0; i < n;) {
			if (!bl) {
				printFoo.run();
				bl = true;
				i++;
			} else { //讓出這一輪迴圈,等待重新進入迴圈
				Thread.yield();
			}
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {
		for (int i = 0; i < n;) {
			if (bl) {
				printBar.run();
				bl = false;
				i++;
			} else {
				Thread.yield();
			}
		}
	}
}

5. BlockingQueue

class FooBar {
	private int n;
	
	private BlockingQueue<Integer> fooQueue = new LinkedBlockingDeque<>();
	private BlockingQueue<Integer> barQueue = new LinkedBlockingDeque<>(); 
	
	public FooBar(int n) {
		this.n = n;
		fooQueue.add(0); //做為第一個輸出
	}

	public void foo(Runnable printFoo) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			fooQueue.take(); //取出隊頭元素,如果佇列為空則阻塞
			printFoo.run();
			barQueue.add(0);
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			barQueue.take();
			printBar.run();
			fooQueue.add(0);
		}
	}
}

6.使用Synchronized,配合wait和notify進行執行緒的控制

class FooBar {
	private int n;
	private volatile boolean bl;
	private Object lock = new Object();

	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			synchronized (lock) {
				if (bl) {
					lock.wait();
				}
				printFoo.run();
				bl = true;
				lock.notify();
			}
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {
		for (int i = 0; i < n; i++) {
			synchronized (lock) {
				if (!bl) {
					lock.wait();
				}
				printBar.run();
				bl = false;
				lock.notify();
			}
		}
	}
}

7.使用LockSupport,方式上類似於Synchronized