• 开发进阶系列:Java并发之从基础到框架


    一  线程基础
    1、synchronized取得的锁都是对象锁,哪个线程执行synchronized修饰的方法,哪个线程就获得这个方法所属对象的锁。不同对象不同锁,互不影响。
    另一种情况是static静态方法加synchronized表示类级别的锁,锁定.class类。如:
    public static synchronized void printNum(String tag)
    2、脏读
    ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。
    Oracle中undo:相当于日志,数据库在执行DML操作时会把旧值放到undo里,这样可以实现回滚。
    3、synchronized细节
      3.1 synchronized锁重入:当一个线程得到一个对象的锁后,再次请求此对象时可以再次得到该对象的锁。若出现异常,锁自动释放。此时要及时处理!
      3.2 涉及父子继承,synchronized修饰父的方法和子的方法实现加锁,也没问题。
      3.3 synchronized代码块可以对任意的Object加锁(不要对字符串常量加锁,会出现死循环,可以new一个String对象)。
           当对象本身发生了改变,这个锁就变了。如:String loc = “a”; loc = “b”; //这就变了
           但如果对象本身不变,对象的属性值改变,那锁还是不变。如:
          Final Student s = new Student(); s的名字年龄值改变,但还是多个Thread中同一个s调用方法,那还是同步。
    4、Volatile
      Volatile关键字只能让成员变量在多个线程之间可见。但它不能保证变量的原子性,要实现原子性建议使用atomic类的系列对象(注意atomic类只保证本身方法原子性,不保证多次操作的原子性):
      private static AtomicInteger count = new AtomicInteger(0);
      count.incrementAndGet(); // 相当于++。
      volatile算轻量级synchronized,性能强于synchronized,不会造成阻塞。
    5、wait、notify
      使用wait、notify实现线程间通信。这两个方法都是Object类的方法,也就是说java为所有的对象都提供了这两个方法。
      它俩必须配合synchronized使用。
      wait方法释放锁,notify方法不释放锁。
    6、ThreadLocal
      线程局部变量。它完全不提供锁,而使用以空间换时间的方式,为每个线程提供变量的独立副本,以保障线程安全。高并发时可以用它。
    7、多线程的单例模式
      需要用dubble check Instance或static inner class。
    8、同步类容器:线程安全的,如Vector、HashTable;
        经典错误ConcurrentModificationException:比如当容器迭代过程中,被并发的修改了内容。
    9、并发类容器:jdk5以后出现,如ConcurrentHashMap代替HashTable,CopyOnWriterArryList代替Vector等等,详见
    10、并发Queue
      Jdk提供两套并发队列实现,这两套都继承Queue:
      10.1 以ConcurrentLinkedQueue为代表:高性能队列,适用于高并发场景下的队列,无锁。
      10.2 以BlockingQueue为代表:阻塞队列
          a. ArrayBlockingQueue有界队列
          b. LinkedBlockingQueue无界队列
          c.SynchronousQueue数据极少,不需要放队列
          这三种自上而下分别对应三种应用场景:数据量很大、不大、很少。

          d. PriorityBlockingQueue基于优先级的阻塞队列,重写comparable排序,无界队列。

          e.DelayQueue到延迟时间,才能从队列获取该元素。

     
    二  线程设计模式
    1. future模式,类似ajax。比如用多个线程执行不同模块,以空间换时间,从而减少程序执行时间。
       举个例子main函数请求一方法发参数过去,那方法会启动一个线程去查数据,并告诉请求方先干别的吧,main函数调一方法接收返回结果时,这方法会wait,直到查到数据才被notify,然后返回结果。
       关键就是用了个wait、notify。   
    1. masterWorker模式,常用的并行计算模式。即系统由两类进程协作工作:master进程和worker进程。Master负责接受和分配任务,worker负责处理子任务。当各个Worker子进程处理完成后,会将结果返回给master,由master做归纳总结。其好处是能将一个大任务分解成若干小任务,并执行,从而提高系统的吞吐量。
    2. 生产-消费模式:生产者线程负责提交用户请求,消费者线程则负责处理生产者提交的任务,在生产者和消费者之间通过共享内存缓存区进行通信。
     
    三  JDK多任务执行框架
    Executor框架:在java.util.Concurrent下,是jdk并发包核心。比较重要的类:Executors,扮演线程工厂。通过Executors,可以创建特定功能的线程池。
    Executors创建线程池方法:
      newFixedThreadPool(),固定线程数,池无空闲,任务就暂缓到任务队列。
      newSingleThreadExecutor(),一个线程的线程池,池无空闲,任务就暂缓到任务队列。

      newCachedThreadPool(),根据实际线程数随时调整池大小,无上限,无任务不创建线程,每个空闲线程60s后自动回收。

      newScheduledThreadPool(),返回SchededExecutorService对象,可指定线程数量。

      

    Executors还提供一个可以自定义线程的方法:public ThreadPoolExecutor();  

      此自定义方法的构造方法对于队列是什么类型的比较关键:

      使用有界队列时,如果有新的任务需要执行,若线程池实际线程数小于corePoolSize,则优先创建线程,若大于corePoolSize,则会将任务加入队列,若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。

      使用无界的任务队列时:LinkedBlockingQueue。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加。若后续仍有新的任务加入,而又没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。
     
      附:spring定时器大都用spring Shedule,而不是spring quartz了。
     
     
    四  Concurrent.util工具类
    1、CyclicBarrier:等所有线程都准备好了一起出发。详见

    2、CountDownLacth:监听初始化操作,初始化完毕,通知主线程继续工作。

    3、Callable和Future使用:即Future模式,它非常适合处理很耗时、很长的业务逻辑,可减小系统响应时间,提高系统的吞吐量。

    4、Semaphore信号量——java层面的限流。适合对系统高并发访问量进行评估:

      4.1  PV page view :页面浏览量,用户每刷新一次就会被记录一次。

      4.2  UV unique visitor:24小时内相同ip客户端只记录一次。

      4.3  QPS query per second:每秒查询数,可根据压力测试得到估值。

      4.4  RT response time:请求响应时间。

    五 (重入锁、读写锁使用)锁的高级深化
    1. 重入锁:在需要进行同步的代码加上锁定。
    2. 读写锁:reentrantReadWriteLock,核心是实现读写分离的锁,在读多写少的高并发访问下,性能高于重入锁。
     
    六  并发框架Disruptor
     
    Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。
    Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。
     
    下载disruptor-3.3.2.jar引入我们的项目既可以开始disruptor之旅。
    在使用之前,首先说明disruptor主要功能加以说明,你可以理解为他是一种高效的"生产者-消费者"模型。也就性能远远高于传统的BlockingQueue容器。
     
    在Disruptor中,我们想实现hello world 需要如下几步骤:
      第一:建立一个Event类
      第二:建立一个工厂Event类,用于创建Event类实例对象
      第三:需要有一个监听事件类,用于处理数据(Event类)
      第四:我们需要进行测试代码编写。实例化Disruptor实例,配置一系列参数。然后我们对Disruptor实例绑定监听事件类,接受并处理数据。
      第五:在Disruptor中,真正存储数据的核心叫做RingBuffer,我们通过Disruptor实例拿到它,然后把数据生产出来,把数据加入到RingBuffer的实例对象中即可。
  • 相关阅读:
    ANT安装
    MAVEN配置教程
    闲笔
    js系列
    微信小程序系列_require
    c++复习系列
    codeblocks系列
    mysql系列
    Google Developer Tools
    数学建模算法(三):神经网络
  • 原文地址:https://www.cnblogs.com/zhaot1993/p/13053101.html
Copyright © 2020-2023  润新知