這世上有三樣東西是別人搶不走的:一是吃進胃里的食物,二是藏在心中的夢想,三是讀進大腦的書
- 分析題目。需要使用兩個線程交替打印奇偶數。
-
- 使用同步鎖解決這個問題
-
- 使用信號量來實現交替打印
- 定義兩個信號量,一個奇數信號量,一個偶數信號量,都初始化為1
- 先用掉偶數的信號量,因為要讓奇數先啟動,等奇數打印完再釋放
信號量實現
- 具體實現思路:
-
- 定義兩個信號量,一個奇數信號量,一個偶數信號量,都初始化為1
-
- 先用掉偶數的信號量,因為要讓奇數先啟動,等奇數打印完再釋放
-
- 具體流程就是 第一次的時候先減掉偶數的信號量 奇數線程打印完成以后用掉奇數的信號量。然后釋放偶數的信號量如此循環
import java.util.concurrent.Semaphore;
/**
* @ClassName AlternatePrinting
* @Author yunlogn
* @Date 2019/5/21
* @Description 交替打印奇偶數
*/
public class AlternatePrinting {
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Semaphore semaphoreOdd = new Semaphore(1);
Semaphore semaphoreEven = new Semaphore(1);
semaphoreOdd.acquire(); //讓奇數先等待啟動,所以先減掉偶數的信號量 等奇數線程來釋放
SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphoreOdd, semaphoreEven);
Thread t1 = new Thread(semaphorePrintEven);
t1.start();
SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphoreOdd, semaphoreEven);
Thread t2 = new Thread(semaphorePrintOdd);
t2.start();
}
/**
* 使用信號量實現
*/
static class SemaphorePrintOdd implements Runnable {
private Semaphore semaphoreOdd;
private Semaphore semaphoreEven;
public SemaphorePrintOdd(Semaphore semaphoreOdd, Semaphore semaphoreEven) {
this.semaphoreOdd = semaphoreOdd;
this.semaphoreEven = semaphoreEven;
}
@Override
public void run() {
try {
semaphoreOdd.acquire();//獲取信號量 semaphoreOdd在初始化的時候被獲取了信號量所以這里被阻塞了,所以會先執行下面的奇數線程
while (true) {
i++;
if (i % 2 == 0) {
System.out.println("偶數線程:" + i);
semaphoreEven.release();//釋放偶數信號量 讓奇數線程那邊的阻塞解除
//再次申請獲取偶數信號量,因為之前已經獲取過,如果沒有奇數線程去釋放,那么就會一直阻塞在這,等待奇數線程釋放
semaphoreOdd.acquire();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class SemaphorePrintEven implements Runnable {
private Semaphore semaphoreOdd;
private Semaphore semaphoreEven;
public SemaphorePrintEven(Semaphore semaphoreOdd, Semaphore semaphoreEven) {
this.semaphoreOdd = semaphoreOdd;
this.semaphoreEven = semaphoreEven;
}
@Override
public void run() {
try {
semaphoreEven.acquire();
while (true) {
i++;
if (i % 2 == 1) {
System.out.println("奇數線程:" + i);
semaphoreOdd.release(); //釋放奇數信號量 讓偶數線程那邊的阻塞解除
//這里阻塞,等待偶數線程釋放信號量
//再次申請獲取奇數信號量,需要等偶數線程執行完然后釋放該信號量,不然阻塞
semaphoreEven.acquire();
}
}
} catch (Exception ex) {}
}
}
}
- 需要注意的是,如果某個線程來不及釋放就異常中斷了,會導致另一個線程一直在等,造成死鎖。 雖然這個異常不在這個問題的考慮范圍內 但是可以使用
finally
來包裹釋放鎖資源
同步鎖打印
- 讓兩個線程使用同一把鎖。交替執行 。
-
- 判斷是不是奇數 如果是奇數進入奇數線程執行打印並加一。然后線程釋放鎖資源。然后讓該線程等待
-
- 判斷是不是偶數,如果是偶數進入偶數線程執行打印並加一。然后線程釋放鎖資源。然后讓該線程等待
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassName AlternatePrinting
* @Author yunlogn
* @Date 2019/5/21
* @Description 交替打印奇偶數
*/
public class AlternatePrinting {
public static AtomicInteger atomicInteger = new AtomicInteger(1);
public static void main(String[] args) throws InterruptedException {
Thread a=new Thread(new AThread());
Thread b=new Thread(new BThread());
a.start();
b.start();
}
public static class AThread implements Runnable {
@Override
public void run() {
while (true) {
synchronized (atomicInteger) {
if (atomicInteger.intValue() % 2 != 0) {
System.out.println("奇數線程:" + atomicInteger.intValue());
atomicInteger.getAndIncrement();
// 奇數線程釋放鎖資源
atomicInteger.notify();
try {
atomicInteger.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
// 奇數線程等待
atomicInteger.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public static class BThread implements Runnable {
@Override
public void run() {
while (true){
synchronized (atomicInteger){
if(atomicInteger.intValue() %2== 0 ){
System.out.println("偶數線程:"+ atomicInteger.intValue());
atomicInteger.getAndIncrement();
// 偶數線程釋放鎖資源
atomicInteger.notify();
try {
atomicInteger.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
// 偶數線程等待
atomicInteger.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
一種更簡單的寫法
public class TheadTest {
public static void main(String[] args) {
PrintDigitThread print1 = new PrintDigitThread((i) -> i % 2 == 1, "thread1");
PrintDigitThread print2 = new PrintDigitThread((i) -> i % 2 == 0, "thread2");
print1.start();
print2.start();
}
}
class ShareData {
public static final AtomicInteger atomicInt = new AtomicInteger(0);
}
class PrintDigitThread extends Thread {
private Predicate<Integer> predicate;
public PrintDigitThread(Predicate<Integer> predicate, String name) {
this.predicate = predicate;
this.setName(name);
}
@Override
public void run() {
int v = ShareData.atomicInt.get();
while (v < 100) {
synchronized (ShareData.atomicInt) {
v = ShareData.atomicInt.get();
if (predicate.test(v)) {
System.out.println(Thread.currentThread().getName() + ":" + v);
ShareData.atomicInt.incrementAndGet();
try {
ShareData.atomicInt.notify();
} catch (Exception ex) {
}
} else {
try {
ShareData.atomicInt.wait();
} catch (Exception ex) {
}
}
}
}
}
}