题目:实现两个线程交替打印123...10的功能
这个题我用四种方法实现,现在分享如下:
1.使用semaphore信号量进行实现
假如面试官让当场撕代码的话,我个人觉得这个是最容易实现的,最不容易出错的的是semaphore 构造函数传入的值表示的是共享资源的数量,semaphore类拥有两个方法,一个是acquire方法,执行此方法之后,获得当前的线程的共享资源,执行完之后当前线程的共享资源数量减1,另一个是release方法,执行此方法之后,释放资源,当前线程的共享资源数量加1,
代码如下:
public class Print123 {
private static Semaphore A = new Semaphore(1);
private static Semaphore B = new Semaphore(0);
static class ThreadA extends Thread {
@Override
public void run() {
try {
for (int i = 1; i < 10; ) {
//A获取信号量,此时A信号量减1,当信号量为0时,无法继续获得信号量
A.acquire();
System.out.print(i);
//B释放信号量,此时B信号量加1,B可以继续获得信号量
B.release();
i += 2;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
try {
for (int i = 2; i <= 10; ) {
B.acquire();
System.out.print(i);
A.release();
i += 2;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();
}
}
2.使用synchronized实现
这个题类似于多线程交替打印ABC的题,要打印一个值,必须获取前一个的锁,这个才能保证打印的有序性。打印123是两个线程,为了保证有序性,当前的线程也必须要获得前一个线程的锁。
代码如下:
public class Print123 extends Thread {
private Object pre;
private Object self;
private int start;
public Print123(Object pre, Object self, int start) {
this.pre = pre;
this.self = self;
this.start = start;
}
@Override
public void run() {
try {
for (int i = start; i < 11; i += 2) {
synchronized (pre) {
synchronized (self) {
System.out.print(i);
self.notifyAll();
}
if (i == 11 || i == 10) {
pre.notifyAll();
} else {
pre.wait();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试类如下:
public class Test {
public static void main(String[] args) throws InterruptedException {
Object a=new Object();
Object b=new Object();
new Print123(a,b,1).start();
Thread.sleep(100);
new Print123(b,a,2).start();
}
}
需要说明要判断是否为最后一次打印,如果是最后一次打印的话,就不用再继续阻塞线程了,否则有可能会导致死锁的发生,本代码的意思是:如果需要打印,先获取pre对象的锁,然后获取self锁,获得之后,打印,一次打印完成之后,唤醒其他等待的线程,然后释放self锁,再释放pre锁,使得当前线程处于阻塞状态,等待其他线程的唤醒
3.使用Reentrantlock
因为要保证两个线程之间的切换,所以我用了一个state作为标志,在两个线程之间切换
public class Print123 {
private static Lock lock = new ReentrantLock();
private static int state = 0;
static class ThreadA extends Thread {
@Override
public void run() {
for (int i = 1; i < 10; ) {
lock.lock();
while (state == 0) {
System.out.print(i);
i += 2;
state = 1;
}
lock.unlock();
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
for (int i = 2; i < 11; ) {
lock.lock();
while (state == 1) {
System.out.print(i);
i += 2;
state = 0;
}
lock.unlock();
}
}
}
public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();
}
}
4使用ReentrantLock+condition+await+signal进行打印
这种方法也比较常用,和相对于第三种方法而言,这种方法使用的是Reentrant自带类的方法
public class Print123 {
private static Lock lock = new ReentrantLock();
private static Condition A = lock.newCondition();
private static Condition B = lock.newCondition();
static class ThreadA extends Thread {
@Override
public void run() {
lock.lock();
try {
for (int i = 1; i < 10; i += 2) {
System.out.print(i);
B.signal();
if (i == 10) {
A.signal();
} else {
A.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
lock.lock();
try {
for (int i = 2; i < 11; i += 2) {
System.out.print(i);
A.signal();
if (i == 10) {
B.signal();
} else {
B.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
new ThreadA().start();
new ThreadB().start();
}
以上是我整理的,如果有问题,欢迎指正