两个线程交替打印12345678910

题目:实现两个线程交替打印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();
    }

以上是我整理的,如果有问题,欢迎指正

你可能感兴趣的:(多线程)