• JDK并发包-copy


    整理自炼数成金

    源码连接:

    1. 各种同步控制工具的使用

        1.1.ReentrantLock

            1.1.1. 可重入 单线程可以重复进入,但要重复退出

            1.1.2. 可中断 lockInterruptibly()

            1.1.3. 可限时 超时不能获得锁,就返回false,不会永久等待构成死锁

            1.1.4. 公平锁 先来先得 (相对不公平锁,线程调度的效率会差点)

            

    [java] view plain copy

    public ReentrantLock(boolean fair)  
      
    public static ReentrantLock fairLock = new ReentrantLock(true);  
     

            公平是有代价的。如果您需要公平,就必须付出代价,但是请不要把它作为您的默认选择。

            1.1.5. 代码示例

            

    [java] view plain copy

    Public class Test{  
                Lock lock = new ReentrantLock();    
                Public void Method1(){  
                    try {     
                        lock.lockInterruptibly ();   
                        // update object state    
                    }  
                    finally {  
                        lock.unlock();     
                    }  
                }  
        }  
     

            可以看到 Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。

        1.2.Condition

            1.2.1. 概述

            类似于 Object.wait()和Object.notify()

            与ReentrantLock结合使用

                 Lock 对象则充当绑定到这个锁的条件变量的工厂对象,与标准的 wait 和 notify 方法不同,对于指定的 Lock ,可以有不止一个条件变量与它关联。这样就简化了许多并发算法的开发。例如, 条件(Condition) 的 Javadoc 显示了一个有界缓冲区实现的示例,该示例使用了两个条件变量,“not full”和“not empty”,它比每个 lock 只用一个 wait 设置的实现方式可读性要好一些(而且更有效)。

            1.2.2. 主要接口

            

    [java] view plain copy

    void await() throws InterruptedException;  
      
    void awaitUninterruptibly();  
      
    long awaitNanos(long nanosTimeout) throws InterruptedException;  
      
    boolean await(long time, TimeUnit unit) throws InterruptedException;  
      
     boolean awaitUntil(Date deadline) throws InterruptedException;  
      
    void signal();  
      
    void signalAll();  
     

            1.2.3. API详解

                await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()时或者signalAll()方法时,线 程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。这和Object.wait()方法很相似。

                awaitUninterruptibly()方法与await()方法基本相同,但是它并不会再等待过程中响应中断。

                singal()方法用于唤醒一个在等待中的线程。相对的singalAll()方法会唤醒所有在等待中的线程。这和Obej ct.notify()方法很类似。

            1.2.4.代码示例

            

    [java] view plain copy

    public class BlockQueue<T> {  
        private ReentrantLock lock = new ReentrantLock();  
        private Condition FullCondition = lock.newCondition();  
        private Condition EmptyCondition = lock.newCondition();  
        private List<T> list = new ArrayList<T>() ;  
        private int Max;  
          
        public BlockQueue(int size){  
            Max = size;  
        }  
          
        public boolean push(T t){  
            try{  
                lock.lock();  
                while(list.size()>=Max){  
                    FullCondition.await();  
                }  
                list.add(t);  
                EmptyCondition.signal();  
            }catch(Exception e){  
                e.printStackTrace();  
            }finally{  
                lock.unlock();  
            }  
    //      synchronized(list){  
    //          while(true){  
    //              if(list.size()>=Max){  
    //                  try{  
    //                      list.wait();  
    //                  }catch(Exception e){  
    //                      e.printStackTrace();  
    //                  }  
    //              }else{  
    //                  break;  
    //              }  
    //          }  
    //          list.add(t);  
    //            
    //          list.notify();  
    //      }  
            return true;  
        }  
          
      
          
        public T poll(){  
            T t = null;  
            try{  
                lock.lock();  
                while(list.isEmpty()){  
                    EmptyCondition.await();  
                }  
                t = list.get(0);  
                list.remove(0);  
                FullCondition.signal();  
            }catch(Exception e){  
                e.printStackTrace();  
            }finally{  
                lock.unlock();  
            }  
    //      synchronized(list){  
    //          while(true){  
    //              if(list.isEmpty()){  
    //                  try{  
    //                      list.wait();  
    //                  }catch(Exception e){  
    //                      e.printStackTrace();  
    //                  }  
    //              }else{  
    //                  break;  
    //              }  
    //          }  
    //          t = list.get(0);  
    //          list.remove(0);  
    //          list.notify();  
    //      }  
              
            return t;  
        }  
    }  
     

            该示例是自定义阻塞队列的实现,从代码可以看出,Object. notify ()会对该Object所有wait队列中随机唤醒一个,可能唤醒的不是我们想要的,例如读操作唤醒了读操作,所以可能会造成死锁。而Condition可以有多个,所以可以有目的的唤醒操作。例如只唤醒读操作或写操作。

        1.3. Semaphore(信号量) 

            1.3.1. 概述 共享锁 运行多个线程同时临界区

            1.3.2. 主要接口

                

    [java] view plain copy

    public void acquire()  
      
    public void acquireUninterruptibly()  
      
    public boolean tryAcquire()  
      
    public boolean tryAcquire(long timeout, TimeUnit unit)  
      
    public void release()  
     

            1.3.3.代码示例

                

    [java] view plain copy

    public class TestSemaphore {  
                    public static void main(String[] args) {  
                    // 线程池  
                    ExecutorService exec = Executors.newCachedThreadPool();  
                    // 只能5个线程同时访问  
                    final Semaphore semp = new Semaphore(5);  
                     // 模拟20个客户端访问  
                     for (int index = 0; index < 20; index++) {  
                                  final int NO = index;  
                                  Runnable run = new Runnable() {  
                                                     public void run() {  
                                                                try {  
                                                                        // 获取许可  
                                                                        semp.acquire();  
                                                                        System.out.println("Accessing: " + NO);  
                                                                        Thread.sleep((long) (Math.random() * 10000));  
                                                                        // 访问完后,释放  
                                                                        semp.release();  
                                                                        System.out.println("-----------------"+semp.availablePermits());  
                                                                } catch (InterruptedException e) {  
                                                                        e.printStackTrace();  
                                                                }  
                                                      }  
                                          };  
                          exec.execute(run);  
                 }  
                 // 退出线程池  
                 exec.shutdown();  
           }  
    }  
     

            Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。

        1.4.ReadWriteLock

            1.4.1. 概述

                ReadWriteLock是JDK5中提供的读写分离锁

            读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

            1.4.2. 访问情况

            读-读不互斥:读读之间不阻塞。

            读-写互斥:读阻塞写,写也会阻塞读。

            写-写互斥:写写阻塞。

                 

            1.4.3. 主要接口

    [java] view plain copy

    private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();  
      
    private static Lock readLock = readWriteLock.readLock();  
      
    private static Lock writeLock = readWriteLock.writeLock();  
     

            1.4.4. 代码示例 

    [java] view plain copy

    class Queue3{  
        private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。  
        private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();  
        public void get(){  
            rwl.readLock().lock();//上读锁,其他线程只能读不能写  
            System.out.println(Thread.currentThread().getName() + " be ready to read data!");  
            try {  
                Thread.sleep((long)(Math.random()*1000));  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }finally{  
                rwl.readLock().unlock(); //释放读锁,最好放在finally里面  
            }  
            System.out.println(Thread.currentThread().getName() + "have read data :" + data);         
              
        }  
        public void put(Object data){  
            rwl.writeLock().lock();//上写锁,不允许其他线程读也不允许写  
            System.out.println(Thread.currentThread().getName() + " be ready to write data!");                     
            try {  
                Thread.sleep((long)(Math.random()*1000));  
                this.data = data;    
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }finally{  
                rwl.writeLock().unlock();//释放写锁   
            }       
            System.out.println(Thread.currentThread().getName() + " have write data: " + data);                     
        }  
    }  
     

            代码中get方法可以多个线程同时访问,当put方法被调用时会等正在进行的get方法全部释放了读锁,然后争取到写锁。但是写锁只能1个线程同时拥有,读锁可以多个线程同时拥有。

        1.5.CountDownLatch  

            1.5.1. 概述 

            倒数计时器 

            一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检查。 只有等所有检查完毕后,引擎才能点火。这种场景就非常适合使用CountDownLatch。它可以使得点火线程 ,等待所有检查线程全部完工后,再执行主线程 

            1.5.2. 主要接口

    [java] view plain copy

    static final CountDownLatch end = new CountDownLatch(10);  
      
    end.countDown();  
      
    end.await();  
     

            1.5.3. 示意图

             

            1.5.4. 代码示例

    [java] view plain copy

    public class Test {  
      
         public static void main(String[] args) {    
      
             final CountDownLatch latch = new CountDownLatch(2);  
      
       
      
             new Thread(){  
      
                 public void run() {  
      
                     try {  
      
                         System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");  
      
                        Thread.sleep(3000);  
      
                        System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");  
      
                        latch.countDown();  
      
                    } catch (InterruptedException e) {  
      
                        e.printStackTrace();  
      
                    }  
      
                 };  
      
             }.start();  
      
       
      
             new Thread(){  
      
                 public void run() {  
      
                     try {  
      
                         System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");  
      
                         Thread.sleep(3000);  
      
                         System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");  
      
                         latch.countDown();  
      
                    } catch (InterruptedException e) {  
      
                        e.printStackTrace();  
      
                    }  
      
                 };  
      
             }.start();  
      
       
      
             try {  
      
                 System.out.println("等待2个子线程执行完毕...");  
      
                latch.await();  
      
                System.out.println("2个子线程已经执行完毕");  
      
                System.out.println("继续执行主线程");  
      
            } catch (InterruptedException e) {  
      
                e.printStackTrace();  
      
            }  
      
         }  
      
    }  
     

        1.6.CyclicBarrier

            1.6.1. 概述 

            Cyclic意为循环,也就是说这个计数器可以反复使用。比如,假设我们将计数器设置为10。那么凑齐第一批1 0个线程后,计数器就会归零,然后接着凑齐下一批10个线程

            1.6.2. 主要接口

                public CyclicBarrier(int parties, Runnable barrierAction)

                    barrierAction就是当计数器一次计数完成后,系统会执行的动作 await()

            1.6.3. 示意图

             

            1.6.4. 代码示例

    [java] view plain copy

    public class Test {  
        public static void main(String[] args) {  
            int N = 4;  
            CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {  
                @Override  
                public void run() {  
                    System.out.println("当前线程"+Thread.currentThread().getName());     
                }  
            });  
       
            for(int i=0;i<N;i++)  
                new Writer(barrier).start();  
        }  
        static class Writer extends Thread{  
            private CyclicBarrier cyclicBarrier;  
            public Writer(CyclicBarrier cyclicBarrier) {  
                this.cyclicBarrier = cyclicBarrier;  
            }  
       
            @Override  
            public void run() {  
                System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");  
                try {  
                    Thread.sleep(5000);      //以睡眠来模拟写入数据操作  
                    System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");  
                    cyclicBarrier.await();  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }catch(BrokenBarrierException e){  
                    e.printStackTrace();  
                }  
                System.out.println("所有线程写入完毕,继续处理其他任务...");  
            }  
        }  
    }  
     

            输出结果:

            线程Thread-0正在写入数据...

            线程Thread-3正在写入数据...

            线程Thread-2正在写入数据...

            线程Thread-1正在写入数据...

            线程Thread-2写入数据完毕,等待其他线程写入完毕

            线程Thread-0写入数据完毕,等待其他线程写入完毕

            线程Thread-3写入数据完毕,等待其他线程写入完毕

            线程Thread-1写入数据完毕,等待其他线程写入完毕

         当前线程Thread-3

            所有线程写入完毕,继续处理其他任务...

            所有线程写入完毕,继续处理其他任务...

            所有线程写入完毕,继续处理其他任务...

            所有线程写入完毕,继续处理其他任务...

            从上面输出结果可以看出,每个写入线程执行完写数据操作之后,就在等待其他线程写入操作完毕。

    当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。

        1.7. LockSupport

            1.7.1. 概述 

            提供线程阻塞原语

            1.7.2. 主要接口 

                    LockSupport.park(); 

                    LockSupport.unpark(t1);

            1.7.3. 与suspend()比较 

            不容易引起线程冻结

            1.7.4. 中断响应

            能够响应中断,但不抛出异常。 中断响应的结果是,park()函数的返回,可以从Thread.interrupted()得到中断标志

        1.8.ReentrantLock 的实现

            1.8.1. CAS状态

            1.8.2. 等待队列  

            1.8.3. park()

    2. 并发容器

        2.1.集合包装

            为集合类包了一层衣服,为每个方法添加了同步的实现。

        2.1.1. HashMap

            Collections.synchronizedMap

            public static Map m=Collections.synchronizedMap(new HashMap());

        2.1.2. List

            synchronizedList

        2.1.3. Set

            synchronizedSet

        2.2.ConcurrentHashMap

         高性能HashMap

         看起来是一个Map数据结构,内部实现将其划分为几个小的Map,而且每个Map都有独立的锁,形成分区锁。每个分区的读写操作不影响另一个分区。

        2.3.BlockingQueue

         阻塞队列
    ————————————————
    版权声明:本文为CSDN博主「莱宝」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_21508727/java/article/details/80617634

  • 相关阅读:
    vue-待办日历和Table
    vue-播种量小工具总结
    20200415-巡检发现的有趣事情
    vue-element-admin学习笔记--Setting保存到cookie
    vue-element-admin学习笔记--权限加载及自定义布局(8)
    vue-element-admin学习笔记--权限加载及自定义布局(7)
    vue-element-admin学习笔记--权限加载及自定义布局(6)
    第六天 二维数组 方法
    第五天(冒泡排序)
    第四天 数组(基础)
  • 原文地址:https://www.cnblogs.com/hanease/p/13220633.html
Copyright © 2020-2023  润新知