一道面试题:

启动两个线程, 一个输出 1,3,5,7…99, 另一个输出 2,4,6,8…100 最后 STDOUT 中按序输出 1,2,3,4,5…100

错误实现1:

public class NotifyErrorTest {
    private int i = 1;

    Thread t1 = new Thread(){

        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    notify();
                    if (i <= 100) {
                        System.out.println(currentThread().getName() + ":" + i);
                        i++;
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    };

    Thread t2 = new Thread(){

        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    notify();
                    if (i <= 100) {
                        System.out.println(currentThread().getName() + ":" + i);
                        i++;
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    };

    public static void main(String[] args){
        NotifyErrorTest test = new NotifyErrorTest();

        test.t1.start();
        test.t2.start();
    }
}

结果:

Thread-0:1
Thread-1:1

打印出这两个后,线程就一直被挂起了。为什么会这样呢。
先不考虑这种难看的重复代码需不需要重构,本身代码就有问题,虽然看起来都用了this,但是其实两个this所表示的含义不同,我们两个线程里面加上如下代码

System.out.println(this.getClass());

会发现打印出

class pers.marscheng.thread.NotifyErrorTest$1
class pers.marscheng.thread.NotifyErrorTest$2

原来两个this不是同一个对象,匿名类会生成新的对象,所以导致两个线程获取的monitor锁是不同的。这就导致wait()方法调用之后,两个线程都被挂起,但是再也没人能把他们唤醒,而且由于锁不同,两个线程都同时执行了,打印出的都是1。

正确实现:

public class NotifyTest implements Runnable {
    int i = 1;


    public static void main(String[] args) {
        NotifyTest test = new NotifyTest();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);

        t1.start();
        t2.start();


    }

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                this.notify();
                if (i <= 100) {
                    String threadName = Thread.currentThread().getName();
                    System.out.println(threadName + ":" + i);
                    i++;
                    try {
                        this.wait();

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

通过condition实现:

public class ConditionTest implements Runnable{
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    int i = 1;

    @Override
    public void run() {
        try {
            lock.lock();
            while (true) {
                condition.signal();
                if (i <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                    i++;
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        } finally {
            lock.unlock();
        }
    }


    public static void main(String[] args) {
        ConditionTest test = new ConditionTest();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);

        t1.start();
        t2.start();
    }
}

拓展:

启动三个线程, 一个输出 1,4,7,10…100, 一个输出 2,5,8,11…101,最后一个暑促3,6,9,12...102 最后 STDOUT 中按序输出 1,2,3,4,5…102

实现:

public class NotifyTest2 implements Runnable {
    private Object prev;
    private Object self;
    AtomicInteger i;

    private NotifyTest2(AtomicInteger num,Object prev, Object self) {
        this.i = num;
        this.prev = prev;
        this.self = self;
    }


    @Override
    public void run() {
        while (true) {
            synchronized (prev) {
                synchronized (self) {
                    if (i.get() <= 102) {
                        System.out.println(Thread.currentThread().getName() + ":" + i.get());
                        i.getAndIncrement();
                        self.notify();
                    }
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args)  {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        AtomicInteger num = new AtomicInteger(1);
        NotifyTest2 testA = new NotifyTest2(num,c,a);
        NotifyTest2 testB = new NotifyTest2(num,a,b);
        NotifyTest2 testC = new NotifyTest2(num,b,c);

        new Thread(testA).start();
        new Thread(testB).start();
        new Thread(testC).start();
    }
}

利用AtomicInteger做为共享变量。

posted on 2019-02-08 10:47  白衣苍狗汪汪汪  阅读(588)  评论(0编辑  收藏  举报