实现方法一,信号量
思路:
两个线程都持有相同的一个信号量,奇数线程启动,获取信号量,然后打印完,释放信号量,再休眠,然后偶数线程启动,获取信号量,然后打印完,释放信号量
package demo6;
import java.util.concurrent.Semaphore;
/**
* @ClassName AlternatePrinting
* @Author laixiaoxing
* @Date 2019/3/17 下午5:43
* @Description 交替打印奇偶数
* @Version 1.0
*/
public class AlternatePrinting {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphore);
Thread t2 = new Thread(semaphorePrintEven);
t2.start();
SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphore);
Thread t1 = new Thread(semaphorePrintOdd);
t1.start();
}
/**
* 使用信号量实现
*/
static class SemaphorePrintOdd implements Runnable {
int i = 0;
private Semaphore semaphore;
public SemaphorePrintOdd(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
while (true) {
i++;
if (i % 2 == 0) {
System.out.println("偶数线程:"+i);
semaphore.release();
Thread.sleep(2000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class SemaphorePrintEven implements Runnable {
int i = 0;
private Semaphore semaphore;
public SemaphorePrintEven(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
while (true) {
i++;
if (i % 2 == 1) {
System.out.println("奇数线程:"+i);
semaphore.release();
Thread.sleep(2000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
看起来没啥问题,但是!!! 那个休眠特别重要,因为如果不设置休眠时间,或者休眠设置的不合理,就不会是交替打印了。
因为,有这种情况,奇数线程打印完释放信号量,它如果有时间片执行 就会尝试再次去获取信号量,如果获取成功,那就又是奇数线程执行。
比如说去掉thread.sleep之后
public class AlternatePrinting {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphore);
Thread t2 = new Thread(semaphorePrintEven);
t2.start();
SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphore);
Thread t1 = new Thread(semaphorePrintOdd);
t1.start();
}
/**
* 使用信号量实现
*/
static class SemaphorePrintOdd implements Runnable {
int i = 0;
private Semaphore semaphore;
public SemaphorePrintOdd(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
while (true) {
i++;
if (i % 2 == 0) {
System.out.println("偶数线程:"+i);
semaphore.release();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class SemaphorePrintEven implements Runnable {
int i = 0;
private Semaphore semaphore;
public SemaphorePrintEven(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
while (true) {
i++;
if (i % 2 == 1) {
System.out.println("奇数线程:"+i);
semaphore.release();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
会发现持续都是偶数或者奇数的情况!
那么怎么优化? 这里暂时只考虑信号量的实现方式。
那么就只能让奇数线程持有偶数线程的信号量,当奇数线程执行完之后,释放,偶数线程就可以执行,然后偶数线程持有奇数线程的信号量,执行完释放。
具体实现:
定义两个信号量,一个奇数信号量,一个偶数信号量,都初始化为1
先用掉偶数的信号量,因为要让奇数先启动,等奇数打印完再释放
奇数线程获取奇数信号量,执行打印,释放偶数线程的信号量,再次请求获取奇数信号量(因为之前已经获取过,因此会阻塞,除非收到偶数线程的释放)
偶数线程获取偶数信号量,执行打印,释放奇数信号量, 再次请求获取偶数信号量
package demo6;
import java.util.concurrent.Semaphore;
/**
* @ClassName AlternatePrinting
* @Author laixiaoxing
* @Date 2019/3/17 下午5:43
* @Description 交替打印奇偶数
* @Version 1.0
*/
public class AlternatePrinting {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphoreOld = new Semaphore(1);
Semaphore semaphoreEven = new Semaphore(1);
semaphoreOld.acquire();//让奇数先启动,所以先减掉偶数的信号量 等奇数线程来释放
SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphoreOld, semaphoreEven);
Thread t1 = new Thread(semaphorePrintEven);
t1.start();
SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphoreOld, semaphoreEven);
Thread t2 = new Thread(semaphorePrintOdd);
t2.start();
}
/**
* 使用信号量实现
*/
static class SemaphorePrintOdd implements Runnable {
int i = 0;
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();//获取信号量
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 {
int i = 0;
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) {}
}
}
}
需要注意的是,如果某个线程来不及释放就异常中断了,会导致另一个线程一直在等,造成死锁。 虽然这个异常不在这个问题的考虑范围内