多线程基础(三)
一、线程通信
多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。
1、共享内存方式通信(while轮询的方式)
public class ListAdd1 {
    private static volatile List<String> list = new ArrayList<>();
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            for (int i = 0; i <10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add("element");
                System.out.println(Thread.currentThread().getName() + "向list中添加了" + (i + 1) + "个元素");
            }
        },"t1");
        Thread t2 = new Thread(()->{
            System.out.println("进入t2线程...");
            while(true) {
                if (list.size() ==5) {
                    System.out.println("t2线程收到通知,线程结束...");
                    throw new RuntimeException();
                }
            }
        },"t2");
        t2.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.start();
    }
}这种方式t1线程会不断地改变条件,线程t2不停的通过while语句检测list1.size() == 5是否成立,从而实现线程间的通信。但是这种方式会浪费CPU的资源。
2、wait/notify机制
使用wait/notify方法实现线程间的通信。(注意这两个方法都是object类的方法,换句话说java为所有的对象都提供了这两个方法)
① wait和notify必须配合synchronized关键字使用
② wait方法释放锁,notify方法不释放锁
③ wait和notify要被同一个对象调用
public class ListAdd1 {
    private static volatile List<String> list = new ArrayList<>();
    public static void main(String[] args) {
        //实例化出来一个 lock,当使用wait 和 notify 的时候 , 一定要配合着synchronized关键字去使用
        final Object lock = new Object();
        Thread t1 = new Thread(()->{
           synchronized (lock){
               for (int i = 0; i <10; i++) {
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   list.add("element");
                   System.out.println(Thread.currentThread().getName() + "向list中添加了" + (i + 1) + "个元素");
                   if (i == 4) {
                       System.out.println("t1向t2发出通知");
                       lock.notify();
                   }
               }
           }
        },"t1");
        Thread t2 = new Thread(()->{
            synchronized (lock) {
                System.out.println("进入t2线程...");
                if (list.size() != 5) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2线程收到通知,线程结束...");
                throw new RuntimeException();
            }
        },"t2");
        t2.start();
        t1.start();
    }
}这种方式实时性比较差,可以使用CountDownLatch进行改进。
public class ListAdd1 {
    private static volatile List<String> list = new ArrayList<>();
    public static void main(String[] args) {
       final CountDownLatch countDownLatch = new CountDownLatch(1);
        Thread t1 = new Thread(()->{
               for (int i = 0; i <10; i++) {
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   list.add("element");
                   System.out.println(Thread.currentThread().getName() + "向list中添加了" + (i + 1) + "个元素");
                   if (i == 4) {
                       System.out.println("t1向t2发出通知");
                       countDownLatch.countDown();
                   }
           }
        },"t1");
        Thread t2 = new Thread(()->{
                System.out.println("进入t2线程...");
                if (list.size() != 5) {
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2线程收到通知,线程结束...");
                throw new RuntimeException();
        },"t2");
        t2.start();
        t1.start();
    }
}二、使用wait/notify模型LinkedBlockQueue
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class MyQueue {
  // 1 需要一个承装元素的集合
  private LinkedList<Object> list = new LinkedList<Object>();
  // 2 需要一个计数器
  private AtomicInteger count = new AtomicInteger(0);
  // 3 需要制定上限和下限
  private final int minSize = 0;
  private final int maxSize;
  // 4 构造方法
  public MyQueue(int size) {
    this.maxSize = size;
  }
  // 5 初始化一个对象 用于加锁
  private final Object lock = new Object();
  // put(anObject):
  // 把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断,直到BlockingQueue里面有空间再继续.
  public void put(Object obj) {
    synchronized (lock) {
      while (count.get() == this.maxSize) {
        try {
          lock.wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      // 1 加入元素
      list.add(obj);
      // 2 计数器累加
      count.incrementAndGet();
      // 3 通知另外一个线程(唤醒)
      lock.notify();
      System.out.println("新加入的元素为:" + obj);
    }
  }
  // take:
  // 取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入.
  public Object take() {
    Object ret = null;
    synchronized (lock) {
      while (count.get() == this.minSize) {
        try {
          lock.wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      // 1 做移除元素操作
      ret = list.removeFirst();
      // 2 计数器递减
      count.decrementAndGet();
      // 3 唤醒另外一个线程
      lock.notify();
    }
    return ret;
  }
  public int getSize() {
    return this.count.get();
  }
  public static void main(String[] args) {
    final MyQueue mq = new MyQueue(5);
    mq.put("a");
    mq.put("b");
    mq.put("c");
    mq.put("d");
    mq.put("e");
    System.out.println("当前容器的长度:" + mq.getSize());
    Thread t1 = new Thread(() -> {
      mq.put("f");
      mq.put("g");
    }, "t1");
    t1.start();
    Thread t2 = new Thread(() -> {
      Object o1 = mq.take();
      System.out.println("移除的元素为:" + o1);
      Object o2 = mq.take();
      System.out.println("移除的元素为:" + o2);
    }, "t2");
    try {
      TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    t2.start();
  }
}三、单例模式
1、double check instance
public class DubbleSingleton {
  private volatile static DubbleSingleton ds;
  // 1、私有构造方法
  private DubbleSingleton() {
  }
  // 2、公共方法内部,双重校验
  public static DubbleSingleton getDs() {
    if (ds == null) {
      try {
        // 模拟初始化对象的准备时间...
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      synchronized (DubbleSingleton.class) {
        if (ds == null) {
          ds = new DubbleSingleton();
        }
      }
    }
    return ds;
  }
  public static void main(String[] args) {
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println(DubbleSingleton.getDs().hashCode());
      }
    }, "t1");
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println(DubbleSingleton.getDs().hashCode());
      }
    }, "t2");
    Thread t3 = new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println(DubbleSingleton.getDs().hashCode());
      }
    }, "t3");
    t1.start();
    t2.start();
    t3.start();
  }
}2、static inner class
public class Singletion {
     private Singletion() {
     }
     private static class InnerSingletion {
           private static Singletion single = new Singletion();
     }
     public static Singletion getInstance() {
           return InnerSingletion.single;
     }
}









