并发编程(二)进阶部分

上节并发编程(一)

  1. synchronized静态方法锁定的是类对象

    import java.util.concurrent.TimeUnit;
    
    public class Demo {
    
        public synchronized static void test1() {
            System.out.println("test1 start");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("test1 end");
        }
    
        public void test2() {
            synchronized (getClass()) {
                System.out.println("test2 start");
            }
        }
    
        public static void main(String[] args) {
            Demo demo = new Demo();
            //t1执行完成后t2执行
            new Thread(Demo::test1,"test1").start();
            new Thread(demo::test2,"test2").start();
        }
    }
    
    
  2. 线程的变量的可见性

    import java.util.concurrent.TimeUnit;
    
    public class TLDemo1 {
    
        volatile static String name = "A";
    
        public static void main(String[] args) {
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                name = "B";
            }).start();
    
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name);
            }).start();
        }
    }
    
  3. ThreadLocal

    import java.util.concurrent.TimeUnit;
    
    /*
    * ThreadLocal线程局部变量
    * threadlocal只对当前线程范围有效,比如下面例子,在第一个线程设置的值,第二个线程是使用不了的
    * */
    public class TLDemo2 {
    
        private static ThreadLocal<User> threadLocal = new ThreadLocal<>();
    
        public static void main(String[] args) {
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                threadLocal.set(new User());
                System.out.println(threadLocal.get());
            }).start();
    
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //threadLocal.set(new User());
                //threadLocal.remove();
                System.out.println(threadLocal.get());
            }).start();
        }
    }
    
  4. 售票的例子

    • 版本一

      import java.util.ArrayList;
      import java.util.List;
      
      /*
      * 有10000张火车票,同时有10个窗口对外售票
      * 请写一个模拟程序
      * */
      public class SaleOfTickets1 {
      
          private static List<Integer> tickets = new ArrayList<>();
      
          static {
              for (int i = 0; i < 10000; i++) {
                  tickets.add(i);
              }
          }
      
          /**
           * 当我们卖到最后一张票的时候,tickets是大于0的,第一个线程看到他大于0,进入判断,
           * 第二个线程同样操作区remove的时候票就没有了,在高并发的情况下,还可能会卖重,
           * 几百万线程去卖票的时候,remove方法不是同步的,第一个线程卖了这张票,第二个线程可能也买了。
           */
          public static void main(String[] args) {
              for (int i = 0; i < 10; i++) {
                  new Thread(() -> {
                      while (tickets.size() > 0) {
                          System.out.println(Thread.currentThread().getName() + "销售票编号" + tickets.remove(0));
                      }
                  }).start();
              }
          }
      }
      
      
    • 版本二

      import java.util.List;
      import java.util.Vector;
      import java.util.concurrent.TimeUnit;
      
      /*
       * 有10000张火车票,同时有10个窗口对外售票
       * 请写一个模拟程序
       * */
      public class SaleOfTickets2 {
      
          private static List<Integer> tickets = new Vector<>();
      
          static {
              for (int i = 0; i < 10000; i++) {
                  tickets.add(i);
              }
          }
      
          /**
           * Vector是一个同步容器
           * 这里是判断和操作分离了,虽然说在vector里面remove方法是原子性的,但是他的对象判断和remove是分离的
           * 但是可能在判断到remove的过程当中线程可能被打断。我们可以加一个模拟性的睡眠,因为在你实际开发的
           * 时候,可能在这中间有些判断代码逻辑代码
           * 如果剩余了最后一个了,很多线程去抢票,虽然size是原子性的,remove是原子性的,但是在他们的中间,
           * 线程还是有可能被打断
           */
          public static void main(String[] args) {
              for (int i = 0; i < 10; i++) {
                  new Thread(() -> {
                      while (tickets.size() > 0) {
                          try {
                              TimeUnit.MILLISECONDS.sleep(10);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          System.out.println(Thread.currentThread().getName() + "销售票编号" + tickets.remove(0));
                      }
                  }).start();
              }
          }
      }
      
    • 版本三

      import java.util.ArrayList;
      import java.util.List;
      
      /*
       * 有10000张火车票,同时有10个窗口对外售票
       * 请写一个模拟程序
       * */
      public class SaleOfTickets3 {
      
          private static List<Integer> tickets = new ArrayList<>();
      
          static {
              for (int i = 0; i < 10000; i++) {
                  tickets.add(i);
              }
          }
      
          public static void main(String[] args) {
              for (int i = 0; i < 10; i++) {
                  new Thread(() -> {
                      while (true) {
                          synchronized (tickets){
                              if (tickets.size() <= 0){
                                  break;
                              }
                              System.out.println("销售票编号" + tickets.remove(0));
                          }
                      }
                  }).start();
              }
          }
      }
      
    • 版本四

      import java.util.Queue;
      import java.util.concurrent.ConcurrentLinkedDeque;
      
      /**
       * 有10000张火车票,同时有10个窗口对外售票
       * 请写一个模拟程序
       *
       * 在JDK1.5以后,java里面提供了很多的并发容器,这里我们用的是一个queue,队列。
       * 所谓队列其实就是一个容器,就是站成一对,不管票还是人都在里面排成一堆,队列有几种,有先进先出的,
       * 还有两端的队列,还有就是栈,先进后出,先加进去的后出来。
       * 这里用了一个concurrentlinkedqueue,并发的链表队列。线程里面调用了一个poll方法,
       * 意思是往外面拿一个数据,相当于在尾巴里面拿一个,如果没有拿到,他的返回值就是空,那么就中断线程。
       * 这里面没有加锁,同样有判断,但是不会出问题。完成卖票功能这种效率是比较高的。queue里面是不能装空值。
       * 这里虽然判断和操作是一起的,但是我们没有在判断里面有任何操作,大不了反过头来再拿一边,
       * poll底层实现是cas,这里我们就不用加锁了。
       */
      
      public class SaleOfTickets4 {
      
          private static Queue<Integer> tickes = new ConcurrentLinkedDeque<>();
      
          static {
              for (int i = 0; i < 10000; i++) {
                  tickes.add(i);
              }
          }
      
          public static void main(String[] args) {
              for (int i = 0; i < 10; i++) {
                  new Thread(()->{
                      while (true){
                          Integer poll = tickes.poll();
                          if (poll == null) {
                              break;
                          }
                          System.out.println("销售票编号:" + poll);
                      }
                  }).start();
              }
          }
      }
      
  5. CopyOnWriteList

    import java.util.*;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    /*
    * CopyOnWriteList 写时复制容器
    *
    * 在往集合中添加数据的时候,先拷贝存储的数组,然后添加元素到拷贝号的数组中,
    * 然后用现在的数组去替换成员变量的数组(就是get等读取操作读取的数组)
    * 这个机制和读写锁是一样的,但是比读写锁有改进的地方,那就是读取的时候可以写入的
    * 这样省去了读写之间的竞争,看了这个过程,你也发现了问题,同时写入的时候怎么办呢,当然果断还是加锁。
    *
    * 读多写少可以用copyOnWriteList
    * */
    public class Demo {
    
        public static void main(String[] args) {
            //List<String> lists = new ArrayList<>();
            List<String> lists = new Vector<>();
            //List<String> lists = new CopyOnWriteArrayList<>();
            Random r = new Random();
            Thread[] threads = new Thread[100];
    
            for (int i = 0; i < threads.length; i++) {
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j < 1000; j++) {
                            lists.add("A" + r.nextInt(10000));
                        }
                    }
                };
                threads[i] = new Thread(task);
            }
            run(threads);
            System.out.println(lists.size());
    
        }
    
        private static void run(Thread[] threads) {
            long start = System.currentTimeMillis();
            Arrays.asList(threads).forEach(Thread::start);
            Arrays.asList(threads).forEach(t->{
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }
    
  6. Collections

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * Collections
     *
     * collections是java里面一个集合处理类,里面有给容器加锁的方法,通过调用api可以返回一个加了锁的容器。
     */
    public class Demo {
    
        public static void main(String[] args) {
            ArrayList<String> arrayList = new ArrayList<>();
            List<String> synchronizedList = Collections.synchronizedList(arrayList);
        }
    }
    
  7. ConcurrentLinkedDeque

    import java.util.Queue;
    import java.util.concurrent.ConcurrentLinkedDeque;
    
    public class Demo {
    
        public static void main(String[] args) {
            Queue<String> strings = new ConcurrentLinkedDeque<>();
            for (int i = 0; i < 10; i++) {
                //offer,类似于add方法,add会出现一些问题,比如容量限制
                //超出限制会抛异常,offer有返回值就可以判断是否加成功了
                strings.offer("a" + i);
            }
    
            System.out.println(strings);
            System.out.println(strings.size());
    
            System.out.println(strings.poll());//拿了就没有了
            System.out.println(strings.size());
    
            System.out.println(strings.peek());//用一下不删
            System.out.println(strings.size());
        }
    }
    
  8. ConcurrentHashMap

    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentSkipListMap;
    import java.util.concurrent.CountDownLatch;
    
    /**
     * ConcurrentHashMap
     * 并发的hashmap,这个例子测试一下效率
     * <p>
     * 第一种用hashtable,hashtable所有方法都加了锁了,
     * 第二种concurrentHashmap,大致能看出来他的效率要比hashtable要高一些,在多线程的情况下。
     * 为什么呢,因为hashtable往里面加任何数据的时候都是要锁定整个对象,
     * 而concurrentHashmap,是分成十六个段,每次插数据的时候,只会锁住一小段,1.8之后实现不同。
     */
    public class Demo {
    
        public static void main(String[] args) {
            //Map<String, String> map = new ConcurrentHashMap<>();
            //Map<String, String> map = new ConcurrentSkipListMap<>();
            //Map<String, String> map = new Hashtable<>();
    
            Map<String, String> map = new HashMap<>();
            Map<String, String> map1 = Collections.synchronizedMap(map);
    
            Random random = new Random();
            Thread[] threads = new Thread[100];
            CountDownLatch latch = new CountDownLatch(threads.length);
            long start_time = System.currentTimeMillis();
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(() -> {
                    for (int j = 0; j < 10000; j++) {
                        //map.put("a" + random.nextInt(100000), "a" + random.nextInt(100000));
                        map1.put("a" + random.nextInt(100000), "a" + random.nextInt(100000));
                    }
                    latch.countDown();
                });
            }
            Arrays.asList(threads).forEach(Thread::start);
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end_time = System.currentTimeMillis();
            System.out.println(end_time - start_time);
    
        }
    }
    
  9. LinkedBlockingQueue

    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingDeque;
    
    /*
    * LinkedBlockingQueue
    * 阻塞式的容器
    * */
    public class Demo {
    
        private static BlockingQueue<String> strings = new LinkedBlockingDeque<>();
    
        public static void main(String[] args) {
            new Thread(() -> {
                for (int i = 0; i < 100; i++) {
                    try {
                        /*
                        * 在阻塞式容器里面加了一个方法,put,也就是如果满了就会等待,对应的方法叫take,如果空了就会等待。
                        * 这种容器我们去用的时候会自动就实现了阻塞式的生产者消费者
                        * */
                        strings.put("商品" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "producer").start();
    
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    for (; ; ) {
                        try {
                            //take,拿,如果空了也会阻塞
                            System.out.println(Thread.currentThread().getName() + "take " + strings.take());//如果空了,就会等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }, "consumer" + i).start();
            }
        }
    }
    
  10. ArrayBlockingQueue

    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.TimeUnit;
    
    /*
    * ArrayBlockingQueue
    *
    * 有界队列,意思就是锁这个队列能装的元素的个数是固定的,后面讲线程的时候,里面装的其实是一个个任务。
    * 这里只能装10个,如果超过了可能会出现问题可能会阻塞,这里看你调用什么方法。
    * add会报异常
    * offer不会报异常,他只通过布尔类型的返回值来告诉你是否添加成功了
    * offer可以设置时间,如果这段时间加不进来就不加了也就是返回false
    * put方法是满了会阻塞住
    * */
    public class Demo {
    
        private static BlockingQueue<String> strings = new ArrayBlockingQueue<>(10);
    
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
                strings.put("a" + i);
            }
            //strings.add("aaaa");
            //strings.put("aaaa");
            strings.offer("aaaa");
            strings.offer("aaaa", 1, TimeUnit.SECONDS);
            System.out.println(strings);
        }
    }
    
    
  11. DelayQueue

    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.DelayQueue;
    import java.util.concurrent.Delayed;
    import java.util.concurrent.TimeUnit;
    
    /*
     * DelayQueue
     * 容器里每一个元素都设置了一个时间,时间到了才能从中提取元素
     * */
    public class Demo {
    
        private static BlockingQueue<MyTask> tasks = new DelayQueue<>();
    
        static class MyTask implements Delayed {
    
            long runningTime;
    
            public MyTask(long rt) {
                this.runningTime = rt;
            }
    
            @Override
            public long getDelay(TimeUnit unit) {
                return unit.convert(runningTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
            }
    
            @Override
            public int compareTo(Delayed o) {
                /*if (this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MICROSECONDS)) {
                    return -1;
                }else if (this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) {
                    return 1;
                }else {
                    return 0;
                }*/
                return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
            }
    
            @Override
            public String toString() {
                return "" + runningTime;
            }
    
            public static void main(String[] args) throws InterruptedException {
                long now = System.currentTimeMillis();
                MyTask t1 = new MyTask(now + 1000);
                MyTask t2 = new MyTask(now + 2000);
                MyTask t3 = new MyTask(now + 1500);
                MyTask t4 = new MyTask(now + 2500);
                MyTask t5 = new MyTask(now + 500);
    
                tasks.put(t1);
                tasks.put(t2);
                tasks.put(t3);
                tasks.put(t4);
                tasks.put(t5);
    
                System.out.println(tasks);
    
                for (int i = 0; i < 5; i++) {
                    System.out.println(tasks.take());
                }
            }
        }
    }
    
  12. TransferQueue

    import java.util.concurrent.LinkedTransferQueue;
    import java.util.concurrent.TimeUnit;
    
    /*
    * TransferQueue
    * 和普通的queue的方法差不多,多了一个transfer方法。
    * 如果你用这个队列的话,往往是消费者先启动,生产者生产一个东西的时候,他先是去找消费者,
    * 如果有消费者就直接丢给消费者
    * */
    public class Demo {
    
        public static void main(String[] args) throws InterruptedException {
            LinkedTransferQueue<String> strings = new LinkedTransferQueue<>();
            new Thread(() -> {
                try {
                    System.out.println("t1" + strings.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    
            new Thread(()->{
                try {
                    System.out.println("t2" + strings.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    
            TimeUnit.SECONDS.sleep(2);
    
            strings.transfer("aaa");
    //		strings.put("aaa");
            System.out.println(strings.size());
    //		new Thread(()->{
    //			try {
    //				System.out.println(strings.take());
    //			} catch (Exception e) {
    //				e.printStackTrace();
    //			}
    //		}).start();
        }
    }
    
  13. SynchronizedQueue

    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.SynchronousQueue;
    
    /**
     * SynchronizedQueue 同步队列
     * 同步队列是容量为0,也就是来的东西必须给消费掉.
     * 首先启动一个消费者,调用add方法,他报错了
     * 只能调用put,意思就是阻塞等待消费者消费。put里面其实用的是transfer,任何东西必须消费,不能往容器里面扔。
     */
    public class Demo {
    
        public static void main(String[] args) throws InterruptedException {
            BlockingQueue<String> strings = new SynchronousQueue<>();
    
            new Thread(()->{
                try {
                    System.out.println(strings.take());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
    //		strings.add("aaa");
            strings.put("aaa");
            strings.put("aaa");
            strings.put("aaa");
            strings.put("aaa");
            strings.put("aaa");
            System.out.println(strings.size());
        }
    }
    

    具体代码见我的资源。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值