淘宝面试题

 

/**

* 曾经的面试题:(淘宝?)

* 实现一个容器,提供两个方法,add,size

* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束

*

* 分析下面这个程序,能完成这个功能吗?

* @author mashibing

*/

package com.mashibing.juc.c_020_01_Interview;

 

 

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.TimeUnit;

 

 

 

 

public class T01_WithoutVolatile {

 

 

    List lists = new ArrayList();

 

 

    public void add(Object o) {

        lists.add(o);

    }

 

 

    public int size() {

        return lists.size();

    }

    

    public static void main(String[] args) {

        T01_WithoutVolatile c = new T01_WithoutVolatile();

 

 

        new Thread(() -> {

            for(int i=0; i<10; i++) {

                c.add(new Object());

                System.out.println("add " + i);

                

                try {

                    TimeUnit.SECONDS.sleep(1);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }, "t1").start();

        

        new Thread(() -> {

            while(true) {

                if(c.size() == 5) {

                    break;

                }

            }

            System.out.println("t2 结束");

        }, "t2").start();

    }

}

 

 

这很明显不能,因为两个线程之间存在不可见效。

淘宝面试题_第1张图片

使用violate和synchronized来解决(假如去掉了睡眠,程序就失效的原因:violate只能保证锁的引用有效,对于引用的属性则不能保证可见性)

/**

* 曾经的面试题:(淘宝?)

* 实现一个容器,提供两个方法,add,size

* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束

*

* 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,

* 而且,如果在if 和 break之间被别的线程打断,得到的结果也不精确,

* 该怎么做呢?

* @author mashibing

*/

package com.mashibing.juc.c_020_01_Interview;

 

 

import java.util.Collections;

import java.util.LinkedList;

import java.util.List;

import java.util.concurrent.TimeUnit;

 

 

 

 

public class T02_WithVolatile {

 

 

    //添加volatile,使t2能够得到通知

    //volatile List lists = new LinkedList();

    volatile List lists = Collections.synchronizedList(new LinkedList<>());

 

 

    public void add(Object o) {

        lists.add(o);

    }

 

 

    public int size() {

        return lists.size();

    }

 

 

    public static void main(String[] args) {

 

 

        T02_WithVolatile c = new T02_WithVolatile();

        new Thread(() -> {

            for(int i=0; i<10; i++) {

                c.add(new Object());

                System.out.println("add " + i);

                

                /*try {

                    TimeUnit.SECONDS.sleep(1);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }*/

            }

        }, "t1").start();

        

        new Thread(() -> {

            while(true) {

                if(c.size() == 5) {

                    break;

                }

            }

            System.out.println("t2 结束");

        }, "t2").start();

    }

}

 

 

 

淘宝面试题_第2张图片

发现结果没有达到预期的根本原因:t1的notify不释放锁,t2只进行了等待操作,没有获取到锁操作

淘宝面试题_第3张图片

 

淘宝面试题_第4张图片

 

解决办法:代码如下

/**

* 曾经的面试题:(淘宝?)

* 实现一个容器,提供两个方法,add,size

* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束

*

* 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?

*

* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁

* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以

*

* 阅读下面的程序,并分析输出结果

* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出

* 想想这是为什么?

*

* notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行

* 整个通信过程比较繁琐

* @author mashibing

*/

package com.mashibing.juc.c_020_01_Interview;

 

 

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.TimeUnit;

 

 

 

 

public class T04_NotifyFreeLock {

 

 

    //添加volatile,使t2能够得到通知

    volatile List lists = new ArrayList();

 

 

    public void add(Object o) {

        lists.add(o);

    }

 

 

    public int size() {

        return lists.size();

    }

    

    public static void main(String[] args) {

        T04_NotifyFreeLock c = new T04_NotifyFreeLock();

        

        final Object lock = new Object();

        

        new Thread(() -> {

            synchronized(lock) {

                System.out.println("t2启动");

                if(c.size() != 5) {

                    try {

                        lock.wait();

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

                System.out.println("t2 结束");

                //通知t1继续执行

                lock.notify();

            }

            

        }, "t2").start();

        

        try {

            TimeUnit.SECONDS.sleep(1);

        } catch (InterruptedException e1) {

            e1.printStackTrace();

        }

 

 

        new Thread(() -> {

            System.out.println("t1启动");

            synchronized(lock) {

                for(int i=0; i<10; i++) {

                    c.add(new Object());

                    System.out.println("add " + i);

                    

                    if(c.size() == 5) {

                        lock.notify();

                        //释放锁,让t2得以执行

                        try {

                            lock.wait();

                        } catch (InterruptedException e) {

                            e.printStackTrace();

                        }

                    }

                    

                    try {

                        TimeUnit.SECONDS.sleep(1);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }

        }, "t1").start();

        

        

    }

}

 

 

淘宝面试题_第5张图片

 

使用CountDownLatch来进行操作。使用两把门栓来进行操作,防止t1count的值为零时当前线程继续运行,而t2线程才开始打印。

/* 曾经的面试题:(淘宝?)

      * 实现一个容器,提供两个方法,add,size

      * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束

      *

      * 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?

      *

      * 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁

      * 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以

      *

      * 阅读下面的程序,并分析输出结果

      * 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出

      * 想想这是为什么?

      *

      * notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行

      * 整个通信过程比较繁琐

      *

      * 使用Latch(门闩)替代wait notify来进行通知

      * 好处是通信方式简单,同时也可以指定等待时间

      * 使用await和countdown方法替代wait和notify

      * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行

      * 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了

      * 这时应该考虑countdownlatch/cyclicbarrier/semaphore

      * @author mashibing

      */

package juc.c_020_01_Interview;

 

 

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.TimeUnit;

 

 

public class T05_CountDownLatch {

 

 

   // 添加volatile,使t2能够得到通知

   volatile List lists = new ArrayList();

 

 

   public void add(Object o) {

      lists.add(o);

   }

 

 

   public int size() {

      return lists.size();

   }

 

 

   public static void main(String[] args) {

      T05_CountDownLatch c = new T05_CountDownLatch();

 

 

      CountDownLatch latch = new CountDownLatch(1);

      CountDownLatch latchNew = new CountDownLatch(1);

      new Thread(() -> {

         System.out.println("t2启动");

         if (c.size() != 5) {

            try {

               latch.await();

 

 

               //也可以指定等待时间

               //latch.await(5000, TimeUnit.MILLISECONDS);

            } catch (InterruptedException e) {

               e.printStackTrace();

            }

         }

         latchNew.countDown();

         System.out.println("t2 结束");

 

 

      }, "t2").start();

 

 

 

 

      try {

         TimeUnit.SECONDS.sleep(1);

      } catch (InterruptedException e1) {

         e1.printStackTrace();

      }

 

 

      new Thread(() -> {

         System.out.println("t1启动");

         for (int i = 0; i < 10; i++) {

            c.add(new Object());

            System.out.println("add " + i);

 

 

            if (c.size() == 5) {

               // 打开门闩,让t2得以执行

               latch.countDown();

               try {

                  latchNew.await();

               } catch (InterruptedException e) {

                  e.printStackTrace();

               }

            }

 

 

//          try {

//             TimeUnit.SECONDS.sleep(1);

//          } catch (InterruptedException e) {

//             e.printStackTrace();

//          }

         }

 

 

      }, "t1").start();

 

 

   }

}

 

 

最简单的办法使用LockSupport

/**

* 曾经的面试题:(淘宝?)

* 实现一个容器,提供两个方法,add,size

* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束

*

* 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢?

*

* 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁

* 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以

*

* 阅读下面的程序,并分析输出结果

* 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出

* 想想这是为什么?

*

* notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行

* 整个通信过程比较繁琐

*

* 使用Latch(门闩)替代wait notify来进行通知

* 好处是通信方式简单,同时也可以指定等待时间

* 使用await和countdown方法替代wait和notify

* CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行

* 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了

* 这时应该考虑countdownlatch/cyclicbarrier/semaphore

* @author mashibing

*/

package com.mashibing.juc.c_020_01_Interview;

 

 

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.LockSupport;

 

 

//TODO park unpark

 

 

public class T07_LockSupport_WithoutSleep {

 

 

    // 添加volatile,使t2能够得到通知

    volatile List lists = new ArrayList();

 

 

    public void add(Object o) {

        lists.add(o);

    }

 

 

    public int size() {

        return lists.size();

    }

 

 

    static Thread t1 = null, t2 = null;

 

 

    public static void main(String[] args) {

        T07_LockSupport_WithoutSleep c = new T07_LockSupport_WithoutSleep();

 

 

        t1 = new Thread(() -> {

            System.out.println("t1启动");

            for (int i = 0; i < 10; i++) {

                c.add(new Object());

                System.out.println("add " + i);

 

 

                if (c.size() == 5) {

                    LockSupport.unpark(t2);

                    LockSupport.park();

                }

            }

        }, "t1");

 

 

        t2 = new Thread(() -> {

            //System.out.println("t2启动");

            //if (c.size() != 5) {

 

 

                LockSupport.park();

 

 

            //}

            System.out.println("t2 结束");

            LockSupport.unpark(t1);

 

 

 

 

        }, "t2");

 

 

        t2.start();

        t1.start();

 

    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(juc)