并发编程(一)基础部分

最近一直在搞并发,但是都是偏于理论和证明的方面,代码写的少之又少,今天主要讲一些并发的代码书写。具体会从通过多个Demo来加深你对并发的理解。

  1. synchronized关键字

    /*
    * synchronized关键字
    * synchronized关键字锁定的是对象不是代码块,demo中锁的是object对象的实例
    * 锁定的对象有两种:1.类的实例,2.类对象(类锁)
    * 加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一
    * */
    public class Demo1 {
        private int count = 10;
        private Object object = new Object();
    
        public void test1() {
            synchronized (object) {
                count--;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
            }
        }
        
        public void test2() {
            //synchronized(this)锁定是当前类的实例,这里锁定的是Demo1的实例
            synchronized (this) {
                count--;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
            }
        }
        
        //直接加在方法声明上,相当于是synchronized(this)
        public synchronized void test3() {
            count --;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
        
        //synchronized关键字修饰静态方法锁定的是类对象(类的.class文件)
        //静态方法中synchronized锁定代码块,锁定的对象不能是类的实例,只能是类对象(类的.class文件)
        public synchronized static void test4() {
            count--;
            System.out.println( "test1 count = " + count);
        }
    }
    
    
  2. 锁的的一些使用规则

    import java.util.concurrent.TimeUnit;
    
    /*
    * 锁对象的改变
    * 锁定某对象object,如果object的属性发生改变,不影响锁的使用
    * 但是如果object变成了另一个对象,则锁定的对象发生改变
    * 应该避免将锁定对象的引用变成另一个对象。
    * */
    public class Demo1 {
        Object object = new Object();
    
        public void test() {
            synchronized (object){
                while (true) {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }
    
        public static void main(String[] args) {
            Demo1 demo = new Demo1();
            new Thread(demo::test, "t1").start();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread t2 = new Thread(demo::test, "t2");
            demo.object = new Object();
            t2.start();
        }
    }
    
    import java.util.concurrent.TimeUnit;
    
    /*
    * 不要以字符串常量作为锁定的对象
    * 在下面,test1和test2其实锁定的是同一个对象,都是存在字符串的常量池中
    * */
    public class Demo2 {
        
        String s1 = "hello";
        String s2 = "hello";
        
        public void test1() {
            synchronized (s1){
                System.out.println("t1 start...");
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end...");
            }
        }
        public void test2() {
            synchronized (s2){
                System.out.println("t2 start...");
            }
        }
    
        public static void main(String[] args) {
            Demo2 demo2 = new Demo2();
            new Thread(demo2::test1, "test1").start();
            new Thread(demo2::test2, "test2").start();
        }
    
    }
    
    import java.util.concurrent.TimeUnit;
    
    /*
    * 同步代码块中的语句越少越好
    * 比较test1和test2
    * 业务逻辑中只有count++这句需要sync,这时不应该给整个方法上锁
    * 采用细粒度的锁,可以使线程争用时间变短,从而提高效率
    * */
    public class Demo3 {
    
        int count = 0;
    
        public synchronized void test1() {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void test2(){
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) {
                count++;
            }
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
  3. 线程重入问题

    /*
    * 问题就在于线程重入的问题
    * 第一个线程减了个1变成9,还没有打印,第二个线程又减了1,第三个线程又减了个1,
    * 这时候虽然第一个线程只减了一个1但是却打印出来一个7(这里的情况是不一定的)
    * 可以给方法加上synchronized
    * */
    public class Demo1 implements Runnable {
    
        private int count = 10;
    
        @Override
        public synchronized void run() {
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    
        public static void main(String[] args) {
            Demo1 demo = new Demo1();
            for (int i = 0; i < 5; i++) {
                new Thread(demo, "THREAD" + i).start();
            }
        }
    }
    
    public class Demo2 implements Runnable {
    
        private int count = 10;
    
        @Override
        public void run() {
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                //相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自标记锁标记,可以同时执行。
                Demo2 demo2 = new Demo2();
                new Thread(demo2, "THREAD" + i).start();
            }
        }
    }
    
    
  4. 面试题

    //同步方法和非同步方法是否可以同时调用? 可以
    public class Demo {
        public synchronized void test1() {
            System.out.println(Thread.currentThread().getName() + " test1 start...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " test1 end ...");
        }
    
        public void test2() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " test2");
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(demo::test1, "test1").start();
            new Thread(demo::test2, "test2").start();
        }
    }
    
  5. 脏读问题

    import java.util.concurrent.TimeUnit;
    /*
    * 脏读问题
    * 实际业务当中应该看是否允许脏读,不允许的情况下对脏读方法也要加锁
    * */
    public class Demo {
    
        String name;
        double balance;
    
        public synchronized void set(String name, double balance) {
            this.name = name;
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.balance = balance;
        }
    
        public /*synchronized*/ double getBalance(String name) {
            return this.balance;
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(() -> demo.set("yesheng", 100.0)).start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(demo.getBalance("yesheng"));
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(demo.getBalance("yesheng"));
        }
    }
    
  6. 面试题

    import java.util.concurrent.TimeUnit;
    
    //一个同步方法调用另一个同步方法,能否得到锁?可以,synchronized本身可支持重入
    public class Demo {
    
        synchronized void test1() {
            System.out.println("test1 start...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            test2();
        }
    
        synchronized void test2() {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("test2 start...");
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            demo.test1();
        }
    }
    
    
  7. 重入锁

    import java.util.concurrent.TimeUnit;
    
    //这里是重入锁的另一种情况,继承
    public class Demo {
    
        synchronized void test() {
            System.out.println("demo test start...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("demo test end....");
        }
    
        public static void main(String[] args) {
            new Demo2().test();
        }
    }
    class Demo2 extends Demo{
        @Override
        synchronized void test() {
            System.out.println("demo2 test start....");
            super.test();
            System.out.println("demo2 test end....");
        }
    }
    
  8. 异常没有处理的情况

    import java.util.concurrent.TimeUnit;
    
    public class Demo {
    
        int count = 0;
    
        synchronized void test() {
            System.out.println(Thread.currentThread().getName() + "start...");
            while (true) {
                count++;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count == 5) {
                    //碰到异常的情况,如果没有处理,会自动释放锁,所以T2可以执行。
                    int i = 1 / 0;
                }
            }
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(demo::test, "t1").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(demo::test, "t2").start();
        }
    }
    
  9. volatile关键字

    /*
    * volatile 关键字,使一个变量在多个线程间可见
    * main,t1线程都用到一个变量,java默认是t1线程中保留一份副本,这样如果main线程修改了该变量,
    * t1线程未必知道
    *
    * 使用volatile关键字,会让所有线程都会读到变量的修改值
    *
    * 在下面的代码中,running是存在于堆内存t1对象中
    * 当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个副本,
    * 并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
    *
    * */
    public class Demo {
    
        volatile boolean running = true;
    
        public void test() {
            System.out.println("test start...");
            while (running) {
    
            }
            System.out.println("test end...");
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            new Thread(demo::test, "t1").start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            demo.running = false;
        }
    }
    
    
    import java.util.ArrayList;
    import java.util.List;
    
    /*
    * 比如说第一个线程加到100了,还没往上加,另外一个线程来了,把100拿过来执行方法,
    * 然后第一个线程继续加到101,第二个线程也加到101,他两往回写都是101,线程不会管你加到哪儿了,
    * 虽然说加了2,但是实际上只加了1
    * volatile并不能多个线程共同修改running变量锁带来的不一致问题
    * 也就是说volatile不能替代synchronized或者说volatile保证不了原子性
    * */
    public class Demo {
    
        volatile int count = 0;
    
        public /*synchronized*/ void test() {
            for (int i = 0; i < 10000; i++) {
                count++;
            }
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            List<Thread> threads = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                threads.add(new Thread(demo::test, "thread- " + i));
            }
    
            threads.forEach(Thread::start);
            threads.forEach(o->{
                try {
                    o.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println(demo.count);
        }
    }
    
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Demo {
        int count = 0;
    
        //相比较上一个例子,synchronized既保证了原子性又保证了可见性
        public synchronized void test() {
            for (int i = 0; i < 10000; i++) {
                count++;
            }
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            List<Thread> threads = new ArrayList<>();
    
            for (int i = 0; i < 10; i++) {
                threads.add(new Thread(demo::test, "thread-" + i));
            }
    
            threads.forEach(Thread::start);
    
            threads.forEach(o->{
                try {
                    o.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println(demo.count);
        }
    }
    
  10. 面试题

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    //一道面试题:多个atomic类连续调用能否构成原子性?
    public class Demo {
    
        AtomicInteger count = new AtomicInteger();
    
        //比如count加到999了,这时候一个线程拿到count判断,虽然get方法保证原子性,但是他阻止
        //不了其他线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了1
        public void test() {
            for (int i = 0; i < 10000; i++) {
                if (count.get() < 1000) {
                    count.incrementAndGet();
                }
            }
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            List<Thread> threads = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                threads.add(new Thread(demo::test, "thread-" + 1));
            }
            threads.forEach(Thread::start);
            threads.forEach(o-> {
                try {
                    o.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println(demo.count);
        }
    }
    
  11. 面试题:实现一个容器,提供两个方法,add size写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束线程2

    • 版本一

      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.TimeUnit;
      
      /*
       * 一道面试题:实现一个容器,提供两个方法,add size
       * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数
       * 当个数到5个时,线程2给出提示并结束线程2
       *
       * 这里list在两个线程之间不保证可见性,所以线程2始终结束不了
       * */
      public class Container1 {
      
          List<Object> lists = new ArrayList<>();
      
          public void add(Object o) {
              lists.add(o);
          }
      
          public int size() {
              return lists.size();
          }
      
          public static void main(String[] args) {
              Container1 c = new Container1();
      
              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();
          }
      }
      
      
    • 版本二:

      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.TimeUnit;
      
      /*
       * 一道面试题:实现一个容器,提供两个方法,add size
       * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数
       * 当个数到5个时,线程2给出提示并结束线程2
       *
       * 有两个问题,第一由于没有加同步,可能size等于5的时候,有另外一个线程加了一下才break,不是很精确
       * 第二个问题就是浪费CPU,T2线程用的死循环
       * */
      public class Container2 {
      
          volatile List<Object> lists = new ArrayList<>();
      
          public void add(Object o) {
              lists.add(o);
          }
      
          public int size() {
              return lists.size();
          }
      
          public static void main(String[] args) {
              Container2 c = new Container2();
              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();
          }
      }
      
    • 版本三:

      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.TimeUnit;
      
      /*
       * 一道面试题:实现一个容器,提供两个方法,add size
       * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数
       * 当个数到5个时,线程2给出提示并结束线程2
       *
       * 这里虽然T2能够及时收到消息唤醒,但是wait会释放锁,notify不会释放锁,所以T1线程结束后
       * T2线程才执行完成
       * */
      public class Container3 {
      
          volatile List<Object> lists = new ArrayList<>();
      
          public void add(Object o) {
              lists.add(o);
          }
      
          public int size() {
              return lists.size();
          }
      
          public static void main(String[] args) {
              Container3 c = new Container3();
              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结束");
                  }
              }, "t2").start();
      
              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();
                          }
      
                          try {
                              TimeUnit.SECONDS.sleep(1);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
      
                      }
                  }
              }, "t1").start();
          }
      }
      
    • 版本四

      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.TimeUnit;
      
      /*
       * 一道面试题:实现一个容器,提供两个方法,add size
       * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数
       * 当个数到5个时,线程2给出提示并结束线程2
       *
       * 相比较上一个例子,这里T1里面用wait释放锁,T2能够及时结束
       * */
      public class Container4 {
      
          volatile List<Object> lists = new ArrayList<>();
      
          public void add(Object o) {
              lists.add(o);
          }
      
          public int size() {
              return lists.size();
          }
      
          public static void main(String[] args) {
      
              Container4 c = new Container4();
              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结束");
                      lock.notify();
                  }
              }, "t2").start();
      
              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();
                              try {
                                  lock.wait();//要释放锁,T2才能得到锁得以执行
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                          try {
                              TimeUnit.SECONDS.sleep(1);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      System.out.println("t1结束");
                  }
              },"t1").start();
          }
      }
      
    • 版本五:

      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.CountDownLatch;
      import java.util.concurrent.TimeUnit;
      
      /*
       * 一道面试题:实现一个容器,提供两个方法,add size
       * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数
       * 当个数到5个时,线程2给出提示并结束线程2
       *
       * CountDownLatch
       * 使用await和countdown方法替代wait和notify
       * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
       * 相当于是法令枪,运动员线程调用await等待,计数到0开始运行
       * 当不涉及同步,只是涉及线程通信的时候,用synchronized加wait,notify就显得太重了
       * */
      public class Container5 {
      
          volatile List<Object> lists = new ArrayList<>();
      
          public void add(Object o) {
              lists.add(o);
          }
      
          public int size() {
              return lists.size();
          }
      
          public static void main(String[] args) {
      
              Container5 c = new Container5();
      
              CountDownLatch latch = new CountDownLatch(1);
      
              new Thread(() -> {
                  System.out.println("t2启动");
                  if (c.size() != 5) {
                      try {
                          latch.await();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println("t2结束");
                  }
              }, "t2").start();
      
              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) {
                          latch.countDown();
                      }
                      try {
                          TimeUnit.SECONDS.sleep(1);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }, "t1").start();
          }
      }
      
  12. 面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法, 能够支持两个生产者线程以及10个消费者线程的阻塞调用wait notifyAll

    • 版本一:

      import java.util.LinkedList;
      import java.util.concurrent.TimeUnit;
      
      /**
       * 面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法,
       * 能够支持两个生产者线程以及10个消费者线程的阻塞调用
       * wait notifyAll
       *
       * 这里探究为什么大多数情况下wait和while是一起使用的,
       * 因为这里是有两个生产者线程并且容器容量是固定的,生产方法加了锁。
       * 如果容器满了,这时候第一个生产者线程拿到了锁,他会判断有没有满,如果满了就等待,在等待
       * 的时候会释放掉锁资源,这时候第二个生产者线程就会拿到锁,然后他也会判断是否满,因为容器是
       * 满了,第二个生产者线程也会等待并且释放锁,当消费者消费之后唤醒所有线程,这时候两个生产者线程
       * 都醒来了,因为要竞争锁资源,比如第一个生产者线程拿到了锁,他给容器又加到十了,陷入等待状态,
       * 锁资源释放掉,第二个生产者线程这时候拿到锁资源,他会继续执行(从上次睡眠的地方继续),如果是if
       * 的话,他在wait阻塞之前就已经执行了一次if,所以不会再执行,而是继续往下执行,那这时候就超过了
       * 容器的容量。所以为了让他再一次判断,这里使用while
       */
      public class Container1<T> {
      
          private final LinkedList<T> lists = new LinkedList<>();
          private final int MAX = 10;
          private int count = 0;
      
          public synchronized void put(T t) {
              while (lists.size() == MAX) {//
                  try {
                      this.wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
      
              lists.add(t);
              ++count;
              this.notifyAll();
          }
      
          public synchronized T get() {
              T t = null;
              while (lists.size() == 0) {
                  try {
                      this.wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
      
              t = lists.removeFirst();
              count--;
              this.notifyAll();
              return t;
          }
      
          public static void main(String[] args) {
              Container1<String> c = new Container1<>();
              for (int i = 0; i < 100; i++) {
                  new Thread(() -> {
                      for (int j = 0; j < 5; j++) {
                          System.out.println(c.get());
                      }
                  }, "c" + i).start();
              }
      
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
      
              for (int i = 0; i < 2; i++) {
                  new Thread(() -> {
                      for (int j = 0; j < 25; j++) {
                          c.put(Thread.currentThread().getName() + " " + j);
                      }
                  }, "p" + i).start();
              }
          }
      
      }
      
      
    • 版本二:

      import java.util.LinkedList;
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.locks.Condition;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      /*
      * 使用Lock和Condition来实现
      * condition就是在什么条件下怎么做
      * 对比上一个例子,Condition的范式可以更加精确的指定那些线程被唤醒
      * */
      public class Container2<T> {
      
          private final LinkedList<T> lists = new LinkedList<>();
          private final int MAX = 10;
          private int count = 0;
      
          private Lock lock = new ReentrantLock();
          private Condition producer = lock.newCondition();
          private Condition consumer = lock.newCondition();
      
          public void put(T t) {
              try {
                  lock.lock();
                  while (lists.size() == MAX) {
                      try {
                          producer.await();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                  lists.add(t);
                  ++count;
                  consumer.signalAll();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  lock.unlock();
              }
          }
      
          public T get() {
              T t = null;
              try {
                  lock.lock();
                  while (lists.size() == 0) {
                      try {
                          consumer.await();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                  t = lists.removeFirst();
                  count--;
                  producer.signalAll();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  lock.unlock();
              }
              return t;
          }
      
          public static void main(String[] args) {
              Container2<String> c = new Container2<>();
              for (int i = 0; i < 100; i++) {
                  new Thread(() -> {
                      for (int j = 0; j < 5; j++) {
                          System.out.println(c.get());
                      }
                  }, "c" + i).start();
              }
      
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
      
              for (int i = 0; i < 2; i++) {
                  new Thread(()->{
                      for (int j = 0; j < 25; j++) {
                          c.put(Thread.currentThread().getName() + " " + j);
                      }
                  }, "p" + i).start();
              }
          }
      }
      
  13. ReentrantLock类的使用

    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /*
    * ReentrantLock用于替代synchronized
    * 使用ReentrantLock可以完成同样的功能
    * ReentrantLock必须要手动释放锁
    * 使用sync锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,
    * 因此经常在finally中进行锁的释放
    * */
    public class RLDemo1 {
    
        Lock lock = new ReentrantLock();
    
        public void test1() {
            try {
                lock.lock();//this
                for (int i = 0; i < 3; i++) {
                    System.out.println(i);
                    TimeUnit.SECONDS.sleep(1);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void test2() {
            lock.lock();
            System.out.println("test2 ...");
            lock.unlock();
        }
    
        public static void main(String[] args) {
            RLDemo1 rlDemo1 = new RLDemo1();
            new Thread(rlDemo1::test1).start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(rlDemo1::test2).start();
        }
    }
    
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class RLDemo2 {
    
        Lock lock = new ReentrantLock();
    
        public void test1() {
            try {
                lock.lock();
                for (int i = 0; i < 2; i++) {
                    System.out.println(i);
                    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        /*
        * 使用ReentrantLock可以进行"尝试锁定tryLock",这样无法锁定,或者在指定时间内无法锁定,
        * 线程可以决定是否继续等待
        * 可以使用tryLock进行尝试锁定,不管锁定是否,方法都将继续执行。
        * 可以根据tryLock的返回值来判读是否锁定
        * 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unlock的处理,必须放在finally中
        * */
        public void test2() {
            boolean locked = false;
            try {
                locked = lock.tryLock(3, TimeUnit.SECONDS);
                System.out.println("test2..." + locked);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (locked){
                    System.out.println("test2 end");
                    lock.unlock();
                }
            }
        }
    
    
        public static void main(String[] args) {
            RLDemo2 rlDemo2 = new RLDemo2();
            new Thread(rlDemo2::test1).start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            new Thread(rlDemo2::test2).start();
        }
    }
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class RLDemo3 {
    
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
    
            Thread t1 = new Thread(() -> {
                try {
                    lock.lock();
                    System.out.println("t1 start");
                    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
                    System.out.println("t1 end");
                } catch (InterruptedException e) {
                    System.out.println("interrupted!");
                } finally {
                    lock.unlock();
                }
            });
            //t1.start();
    
            Thread t2 = new Thread(() -> {
                boolean locked = false;
                try {
                    //lock.lock();
                    //使用lockInterruptibly来锁定可以对Interrupt方法做出响应
                    lock.lockInterruptibly();
                    System.out.println("t2 start");
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println("t2 end");
                    locked = true;
                } catch (InterruptedException e) {
                    System.out.println("interrupted!");
                } finally {
                    if (locked) {
                        lock.unlock();
                    }
                }
            });
            t2.start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            t2.interrupt();
    
        }
    }
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class RLDemo4 extends Thread{
    	//ReentrantLock可以指定是否为公平锁,true为公平,默认为false
    	private static ReentrantLock lock = new ReentrantLock(true);
    
    	@Override
    	public void run() {
    		for (int i = 0; i < 100 ; i++) {
    			lock.lock();
    			try {
    				System.out.println(Thread.currentThread().getName() + "获得锁");
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				lock.unlock();
    			}
    		}
    	}
    	
    	public static void main(String[] args) {
    		RLDemo4 rlDemo4 = new RLDemo4();
    		Thread t1 = new Thread(rlDemo4);
    		Thread t2 = new Thread(rlDemo4);
    		t1.start();
    		t2.start();
    	}
    
    }
    

具体的代码我会放在附件中。可以直接搜索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值