java线程间通信wait/notify 和 CountDownLatch应用

淘宝面试题:
实现一个容器,提供两个方法,add,size,写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当到5个时候,线程2给出提示并结束。
很明显是一个多线程通信问题。

方法1:通过wait-notify,重点:1)wait/await操作释放锁,notify/sinal不释放锁,wait后面的代码要想执行必须获得锁才可以执行  2)wait/await一定要在notify/signal之前执行,如果notify/signal在wait/await之前则无效

 
import java.util.ArrayList;
import java.util.List;
 
public class TaobaoTest {
    public static void main(String[] args) {
        Container container = new Container();
        Object lock = new Object();
        new Thread(() -> {
                synchronized (lock) {
                    System.out.println("t2启动");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("判断是第五个打印");
                    lock.notify(); //唤醒线程t1
 
                }
                return;
 
        },"t2").start();
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("t1启动");
                try {
                    for (int i = 0; i < 10; i++) {
                        if (container.size() == 5) {
                            lock.notify(); //唤醒t2,但是还没有释放锁,所以t2无法执行
                            System.out.println("叫t1打印");
                            lock.wait(); //t1线程睡眠,释放锁,t2线程可以执行
                        }
                        container.add("a");
 
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
 
        },"t1").start();
 
 
    }
 
}
 
/**
 * 容器
 */
class Container {
    private volatile List<String> list =  new ArrayList<>();
 
    public void add(String a) {
        list.add(a);
 
    }
    public int size() {
        return list.size();
    }
}

方法2:CountDownLatch

public class HelloService {

    public static void main(String[] args) throws Exception {
        Container container = new Container();
        final CountDownLatch latch = new CountDownLatch(1);
        new Thread(() -> {
            try {
                latch.await();
            } catch (InterruptedException e) {

            }
            System.out.println("判断是第五个打印");
        },"t2").start();

        Thread.sleep(500);
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("a");
                container.add("a");
                if (container.size() == 5) {
                    latch.countDown();
                }
                try {
                    Thread.sleep(300);
                } catch (Exception e) {

                }
            }
        },"t1").start();
    }
}

/**
 * 容器
 */
class Container {
    private volatile List<String> list =  new ArrayList<>();

    public void add(String a) {
        list.add(a);

    }
    public int size() {
        return list.size();
    }
}

去掉t1线程中的Thread.sleep(300),那就需要在增加一个CountDownLatch,如下:

public static void main(String[] args) throws Exception {
        Container container = new Container();
        //两个CountDownLatch去掉强制Thread
        final CountDownLatch latch = new CountDownLatch(1);
        final CountDownLatch latch2 = new CountDownLatch(1);
        new Thread(() -> {
            try {
                latch.await();
            } catch (InterruptedException e) {

            }
            latch2.countDown();
            System.out.println("判断是第五个打印");
        },"t2").start();

        Thread.sleep(500);
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    System.out.println("a");
                    container.add("a");
                    if (container.size() == 5) {
                        latch.countDown();
                        latch2.await();
                    }
                }
            } catch (InterruptedException e) {

            }

        },"t1").start();
    }

方法3:LockSupport,线程t2必须先执行

public class HelloService {

    static Thread t1 = null, t2 = null;
    public static void main(String[] args) throws Exception {
        Container container = new Container();

        t2 = new Thread(() -> {
            LockSupport.park();
            System.out.println("判断是第五个打印");
            LockSupport.unpark(t1);

        },"t2");


        t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("a");
                container.add("a");
                if (container.size() == 5) {
                    LockSupport.unpark(t2);
                    LockSupport.park();
                }
            }
        },"t1");

        t2.start();//必须保证t2先执行
        Thread.sleep(100);
        t1.start();
    }
}

面试题:两个线程交互打印,最终输出结果为A1B2C3...Z26

1、wait/notify

public class SwitchPrintX {

    static public Object o = new Object();

    static class PrintChar implements Runnable {
        public void run() {
            for (char c = 'A'; c <= 'Z'; c++) {
                synchronized(o) {
                    System.out.print(c);
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {

                    }
                }
            }
        }
    }
    static class PrintNumber implements Runnable {
        public void run() {
            for (int i = 1; i <= 26; i++) {
                synchronized(o) {
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                    }
                    System.out.print(i);
                    o.notify();
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        //print线程先执行, 因为如果不执行,可能会出现notify在wait之前,无法唤醒线程
        new Thread(new PrintNumber()).start();
        Thread.sleep(500);
        new Thread(new PrintChar()).start();
        return;
    }

}

2、Lock和Condition

public class SwitchPrintX {

    static public ReentrantLock lock = new ReentrantLock();
    static public Condition charCondition = lock.newCondition();
    static public Condition numCondition = lock.newCondition();

    static class PrintChar implements Runnable {
        public void run() {
            for (char c = 'A'; c <= 'Z'; c++) {
                lock.lock();
                System.out.print(c);
                numCondition.signal();
                try {
                    charCondition.await();
                } catch (InterruptedException e) {

                }
                lock.unlock();
            }
        }
    }
    static class PrintNumber implements Runnable {
        public void run() {
            for (int i = 1; i <= 26; i++) {
                lock.lock();
                try {
                    numCondition.await();
                } catch (InterruptedException e) {

                }
                System.out.print(i);
                charCondition.signal();
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        //print线程先执行, 因为如果不执行,可能会出现signal在await之前,无法唤醒线程
        new Thread(new PrintNumber()).start();
        Thread.sleep(500);
        new Thread(new PrintChar()).start();
        return;
    }

}

猜你喜欢

转载自blog.csdn.net/xxb249/article/details/115774423