zoukankan      html  css  js  c++  java
  • Java 并发编程实践基础 读书笔记: 第三章 使用 JDK 并发包构建程序

    一,JDK并发包实际上就是指java.util.concurrent包里面的那些类和接口等

      主要分为以下几类: 1,原子量;2,并发集合;3,同步器;4,可重入锁;5,线程池

    二,原子量

      原子变量主要有AtomicInteger,AtomicLong,AtomicBoolean等,

      主要实现原理都是底层实现类CAS 即比较并交换,都有get,set,compareAndSet等方法,如++,--等也都是有自带方法实现

      这些都是线程安全的,保证了多线程访问时候的可见性

      

     1 import java.util.concurrent.atomic.AtomicLong;
     2 
     3 /**
     4  * StudySjms
     5  * <p>
     6  * Created by haozb on 2018/3/2.
     7  */
     8 public class AtomicAccount {
     9 
    10     AtomicLong account;
    11 
    12     public AtomicAccount(long money) {
    13         this.account = new AtomicLong(money);
    14     }
    15 
    16     public void withDraw(long money,int sleepTime){
    17         long oldValue = account.get();
    18         if(oldValue >= money){
    19             try {
    20                 Thread.sleep(sleepTime);
    21             } catch (Exception e) {
    22 
    23             }
    24             if(account.compareAndSet(oldValue,oldValue-money)){
    25                 System.out.println(Thread.currentThread().getName()+"扣钱成功");
    26             }else{
    27                 System.out.println(Thread.currentThread().getName()+"扣钱失败");
    28             }
    29         }else{
    30             System.out.println("钱不够了");
    31         }
    32     }
    33 
    34     public static void main(String[] args) {
    35         final AtomicAccount aa = new AtomicAccount(100);
    36 
    37         for (int i = 0; i < 2; i++) {
    38             new Thread(new Runnable() {
    39                 @Override
    40                 public void run() {
    41                     aa.withDraw(100,1000);
    42                 }
    43             }).start();
    44         }
    45     }
    46 }

    上面这个方法就是利用原子量解决多线程中计数不安全的例子;

    三,并发集合

    这个包里面有一个阻塞队列的接口BlockingQueue,阻塞的概念就是满了就不会再填充了,空了也不允许再取,

    所有实现这个接口的队列都是线程安全的。

    主要有ArrayBlockingQueue:一个由数组支持的有界队列

       LinkedBlockingQueue:一个由链接节点支持的可选有界队列

         PriorityBlockingQueue:一个由优先级堆支持的无界优先级队列

       DelayQueue :一个由优先级堆支持的、基于时间的调度队列

    ConcurrentMap 接口,ConcurrentHashMap, 这个实现类的方法是原子的,源代码里面是采用lock住put那一块代码。

    CopyOnWriteArrayList,CopyOnWriteArraySet采用copy-on-write 模式

    package copyonwrite;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.ConcurrentModificationException;
    import java.util.Iterator;
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class CopyOnWriteDemo {
        @SuppressWarnings("unchecked")
        public static void main(String args[]) {
            String[] ss = {"aa", "bb", "cc"};
            List list1 = new CopyOnWriteArrayList(Arrays.asList(ss));
            List list2 = new ArrayList(Arrays.asList(ss));
            Iterator itor1 = list1.iterator();
            Iterator itor2 = list2.iterator();
            list1.add("New");
            list2.add("New");
            try {
                printAll(itor1);
            } catch (ConcurrentModificationException e) {
                System.err.println("Shouldn't get here");
            }
            try {
                printAll(itor2);
            } catch (ConcurrentModificationException e) {
                System.err.println("Will gethere.ConcurrentModificationException occurs !");
            }
        }
    
        @SuppressWarnings("unchecked")
        private static void printAll(Iterator itor) {
            while (itor.hasNext()) {
                System.out.println(itor.next());
            }
        }
    }

    运行结果如下:
    Will get here.ConcurrentModificationException occurs!
    aa
    bb
    cc

    这个例子很好地说明了。

    四,同步器

      主要有CyclicBarrier:

      1.它允许在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待

      2.重要的属性就是参与者个数,另外最要方法是 await()。当所有线程都调用了 await()后,就表示这些线程都可以继续执行,否则就会等待

     1 package synchronizer;
     2 
     3 import java.text.SimpleDateFormat;
     4 import java.util.Date;
     5 import java.util.concurrent.BrokenBarrierException;
     6 import java.util.concurrent.CyclicBarrier;
     7 import java.util.concurrent.ExecutorService;
     8 import java.util.concurrent.Executors;
     9 
    10 public class CyclicBarrierDemo {
    11     /* 徒步需要的时间: Shenzhen, Guangzhou, Chongqing */
    12     private static int[] timeForWalk = { 5, 8, 15 };
    13     /* 自驾游 */
    14     private static int[] timeForSelf = { 1, 3, 4 };
    15     /* 旅游大巴 */
    16     private static int[] timeForBus = { 2, 4, 6 };
    17 
    18     static String nowTime() /* 时间格式化 */
    19     {
    20         SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm:ss" );
    21         return(sdf.format( new Date() ) + ": ");
    22     }
    23 
    24 
    25     static class Tour implements Runnable {
    26         private int[]        timeForUse;
    27         private CyclicBarrier    barrier;
    28         private String        tourName;
    29 
    30         public Tour( CyclicBarrier barrier, String tourName, int[] timeForUse )
    31         {
    32             this.timeForUse = timeForUse;
    33             this.tourName    = tourName;
    34             this.barrier    = barrier;
    35         }
    36 
    37 
    38         public void run()
    39         {
    40             try {
    41                 Thread.sleep( timeForUse[0] * 1000 );
    42                 System.out.println( nowTime() + tourName + "  ReachedShenenzh " );
    43                 barrier.await();        /* 到达中转站后等待其他旅行团 */
    44                 Thread.sleep( timeForUse[1] * 1000 );
    45                 System.out.println( nowTime() + tourName + "  ReachedGuangzhou" );
    46                 barrier.await();        /* 到达中转站后等待其他旅行团 */
    47                 Thread.sleep( timeForUse[2] * 1000 );
    48                 System.out.println( nowTime() + tourName + "  ReachedChonin" );
    49                 barrier.await();        /* 到达中转站后等待其他旅行团 */
    50             } catch ( InterruptedException e ) {
    51             } catch ( BrokenBarrierException e ) {
    52             }
    53         }
    54     }
    55 
    56     public static void main( String[] args )
    57     {
    58         /* 三个旅行团都到到达某一个站点后,执行下面的操作,表示都到齐了。 */
    59         Runnable runner = new Runnable()
    60         {
    61             @Override
    62             public void run()
    63             {
    64                 System.out.println( "we all are here." );
    65             }
    66         };
    67         CyclicBarrier barrier = new CyclicBarrier( 3, runner );
    68 /* 使用线程池 */
    69         ExecutorService exec = Executors.newFixedThreadPool( 3 );
    70         exec.submit( new Tour( barrier, "WalkTour", timeForWalk ) );
    71         exec.submit( new Tour( barrier, "SelfTour", timeForSelf ) );
    72         exec.submit( new Tour( barrier, "BusTour", timeForBus ) );
    73         exec.shutdown();
    74     }
    75 }
     1 17:13:18: SelfTour Reached Shenzhen
     2 17 3:1 Bus :1 9: Tour Reached Shenzhen
     3 17 3:2 WalkTour Reached Shenzhen  :1 2:
     4 we all are here.
     5 17 3:2 SelfTour Reached Guangzhou  :1 5:
     6 17 3:2 BusTour Reached Guangzhou  :1 6:
     7 17 3:3 WalkTour Reached Guangzhou  :1 0:
     8 we all are here.
     9 17 3:3 SelfTour Reached Ch :1 4:  ongqing
    10 17:13:36: BusTour Reached Chongqing
    11 17:13:45: WalkTour Reached Chongqing
    12 we all are here.

    五,Future(接口)和FutureTask(实现类)

      注:FutureTask实现了Runnable,所以可以通过线程池和Thread执行

      使用他们的好处是可以获得线程的结果,和抛出异常,这种好处通过Callable的定义就知道了

    1 public interface Callable<V> {
    2     /**
    3      * Computes a result, or throws an exception if unable to do so.
    4      *
    5      * @return computed result
    6      * @throws Exception if unable to compute a result
    7      */
    8     V call() throws Exception;
    9 }

    可以通过以下几种方式调用

     1 import java.util.concurrent.ExecutorService;
     2 import java.util.concurrent.Executors;
     3 import java.util.concurrent.Future;
     4 import java.util.concurrent.FutureTask;
     5 
     6 public class CallableTest {
     7 
     8     public static void main(String[] args) {
     9 //      //创建线程池  
    10 //      ExecutorService es = Executors.newSingleThreadExecutor();  
    11 //      //创建Callable对象任务  
    12 //      CallableDemo calTask=new CallableDemo();  
    13 //      //提交任务并获取执行结果  
    14 //      Future<Integer> future =es.submit(calTask);  
    15 //      //关闭线程池  
    16 //      es.shutdown();  
    17 
    18         //创建线程池  
    19         ExecutorService es = Executors.newSingleThreadExecutor();
    20         //创建Callable对象任务  
    21         CallableDemo calTask=new CallableDemo();
    22         //创建FutureTask  
    23         FutureTask<Integer> futureTask=new FutureTask<Integer>(calTask);
    24         //执行任务  
    25         es.submit(futureTask);
    26         //关闭线程池  
    27         es.shutdown();
    28         try {
    29             Thread.sleep(2000);
    30             System.out.println("主线程在执行其他任务");
    31 
    32             if(futureTask.get()!=null){
    33                 //输出获取到的结果  
    34                 System.out.println("futureTask.get()-->"+futureTask.get());
    35             }else{
    36                 //输出获取到的结果  
    37                 System.out.println("futureTask.get()未获取到结果");
    38             }
    39 
    40         } catch (Exception e) {
    41             e.printStackTrace();
    42         }
    43         System.out.println("主线程在执行完成");
    44     }
    45 }  

    六,ReentrantLock显示锁 也叫可重入锁; 必须在finally里面释放锁

      实现方式:

    1 Lock lock=new ReentrantLock();
    2 lock.lock();
    3 try{
    4 // 更新对象状态
    5 }
    6 finally{
    7 lock.unlock();
    8 }

    new的时候,有个参数,true或者false 决定了是不是公平锁,正常上不公平锁的性能比较好!

    线程之间的交互,有一个类叫Condition:Condition 的方法与 wait 、notify 和 notifyAll 方法类似,分别命名为 await 、 signal 和 signalAll

    后面会详细的介绍

    七,ReadWriteLock

    ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的

    这个锁比互斥锁的性能上应该好些(读的频率大于写的频率)

      1 import java.util.Calendar;
      2 import java.util.Map;
      3 import java.util.TreeMap;
      4 import java.util.concurrent.locks.Lock;
      5 import java.util.concurrent.locks.ReentrantReadWriteLock;
      6 
      7 /**
      8  * StudySjms
      9  * <p>
     10  * Created by haozb on 2018/3/2.
     11  */
     12 public class ReadWriteLockDemo {
     13     private ReentrantReadWriteLock lock = null;
     14     private Lock readLock = null;// 读锁
     15     private Lock writeLock = null;// 写锁
     16     public int key = 100;
     17     public int index = 100;
     18     public Map<Integer, String> dataMap = null;// 线程共享数据
     19 
     20     public ReadWriteLockDemo() {
     21         lock = new ReentrantReadWriteLock(true);
     22         readLock = lock.readLock();
     23         writeLock = lock.writeLock();
     24         dataMap = new TreeMap<Integer, String>();
     25     }
     26 
     27     public static void main(String[] args) {
     28         ReadWriteLockDemo tester = new ReadWriteLockDemo();
     29 // 第一次获取锁
     30         tester.writeLock.lock();
     31         System.out
     32                 .println(Thread.currentThread().getName() + " get writeLock.");
     33 // 第二次获取锁,应为是可重入锁
     34         tester.writeLock.lock();
     35         System.out
     36                 .println(Thread.currentThread().getName() + " get writeLock.");
     37         tester.readLock.lock();
     38         System.out.println(Thread.currentThread().getName() + " get readLock");
     39         tester.readLock.lock();
     40         System.out.println(Thread.currentThread().getName() + " get readLock");
     41         tester.readLock.unlock();
     42         tester.readLock.unlock();
     43         tester.writeLock.unlock();
     44         tester.writeLock.unlock();
     45         tester.test();
     46     }
     47 
     48     public void test() {
     49 // 读线程比写线程多
     50         for (int i = 0; i < 10; i++) {
     51             new Thread(new reader(this)).start();
     52         }
     53         for (int i = 0; i < 3; i++) {
     54             new Thread(new writer(this)).start();
     55         }
     56     }
     57 
     58     public void read() {
     59 // 获取锁
     60         readLock.lock();
     61         try {
     62             if (dataMap.isEmpty()) {
     63                 Calendar now = Calendar.getInstance();
     64                 System.out.println(now.getTime().getTime() + " R "
     65                         + Thread.currentThread().getName()
     66                         + " get key, but map is empty.");
     67             }
     68             String value = dataMap.get(index);
     69             Calendar now = Calendar.getInstance();
     70             System.out.println(now.getTime().getTime() + " R "
     71                     + Thread.currentThread().getName() + " key = " + index
     72                     + " value = " + value + " map size = " + dataMap.size());
     73             if (value != null) {
     74                 index++;
     75             }
     76         } finally {
     77 // 释放锁
     78             readLock.unlock();
     79         }
     80         try {
     81             Thread.sleep(3000);
     82         } catch (Exception e) {
     83 
     84         }
     85     }
     86 
     87     public void write() {
     88         writeLock.lock();
     89         try {
     90             String value = "value" + key;
     91             dataMap.put(new Integer(key), value);
     92             Calendar now = Calendar.getInstance();
     93             System.out.println(now.getTime().getTime() + " W "
     94                     + Thread.currentThread().getName() + " key = " + key
     95                     + " value = " + value + " map size = " + dataMap.size());
     96             key++;
     97             try {
     98                 Thread.sleep(500);
     99             } catch (InterruptedException e) {
    100                 e.printStackTrace();
    101             }
    102         } finally {
    103             writeLock.unlock();
    104         }
    105     }
    106 }
    107 
    108 class reader implements Runnable {
    109     private ReadWriteLockDemo tester = null;
    110 
    111     public reader(ReadWriteLockDemo tester) {
    112         this.tester = tester;
    113     }
    114 
    115     @Override
    116     public void run() {
    117         Calendar now = Calendar.getInstance();
    118         System.out.println(now.getTime().getTime() + " R "
    119                 + Thread.currentThread().getName() + " started");
    120         for (int i = 0; i < 10; i++) {
    121             tester.read();
    122         }
    123     }
    124 }
    125 
    126 class writer implements Runnable {
    127     private ReadWriteLockDemo tester = null;
    128 
    129     public writer(ReadWriteLockDemo tester) {
    130         this.tester = tester;
    131     }
    132 
    133     @Override
    134     public void run() {
    135         Calendar now = Calendar.getInstance();
    136         System.out.println(now.getTime().getTime() + " W "
    137                 + Thread.currentThread().getName() + " started");
    138         for (int i = 0; i < 10; i++) {
    139             tester.write();
    140         }
    141     }
    142 }
  • 相关阅读:
    循环逗号分割数组!
    只是想好好学习一下!
    html元素水平垂直居中
    闭包知识点--笔记
    20160314
    从零开始做运维-零
    Nginx 和 CodeIgniter
    安装PIL库
    init
    NutUI3 多端实践之路
  • 原文地址:https://www.cnblogs.com/haoerlv/p/8494379.html
Copyright © 2011-2022 走看看