Java多线程和并发总结

Java多线程和高并发总结

 

    • wait/notify必须存在于synchronized块中。

    • volatile
      多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)

    • Thread类最佳实践: 写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理。在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的。

    • 如何获取线程中异常
      Thread t = new Thread(task);
      t.setUncaughtException(new UncaughtExceptionHandler(){...});
      t.start();
      不能用try,catch来获取线程中的异常

    • Future和FutureTask
      Future是接口,FutureTask是类

      Future表示一个异步计算的结果. 异步计算是在其他线程进行的, 因此异步计算的结果, 有可能有值, 也有可能没有值. 于是, Future就提供了一些方法来处理这种未知状态:
      a. isDone() 异步任务是否完成, 即否有结果
      b. get() 获取异步任务结果, 如果异步任务未完成, 此方法会一直阻塞, 直到异步方法完成 或 任务被取消 (调用了cancel()方法)
      c. cancel() 取消异步任务, 如果异步任务已经完成, 那么取消失败(即cancel()方法返回false)
      d. isCancelled() 查询异步任务是否已被取消

      FutureTask就是一个可取消的异步任务,FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到

      //1
      FutureTask<Integer> future = new FutureTask<Object>(callable);  
      new Thread(future).start();  
      //这里可以做其他事情  
      future.get();//阻塞获得结果  
      
      //2
      public void function() {
          // 1. init services
          final ExecutorService executorService = ...
      
          // 2. make task
          FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
              @Override
              public String call() throws Exception {
                  String result = "";
                  //do something;
                  return result;
              }
          });
      
          // 3. submit task
          executorService.submit(futureTask);
      
          // 4. get result
          String result = futureTask.get();
      
          // 5. do something with result
          // ...
      }
      
    • ThreadLocal类
      用处:保存线程的独立变量。对一个线程类(继承自Thread) 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session信息。

      实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。 主要方法是get()和set(T a),set之后在map里维护一个threadLocal -> a,get时将a返回。ThreadLocal是一个特殊的容器。

    • 并发包:原子类、锁类、容器类、管理类总结
      并发包总结1
      并发包总结2

    • cas和aba
      CAS:对于内存中的某一个值V,提供一个旧值A和一个新值B。如果提供的旧值V和A相等就把B写入V。这个过程是原子性的。
      CAS执行结果要么成功要么失败,对于失败的情形下一班采用不断重试。或者放弃。
      ABA:如果另一个线程修改V值假设原来是A,先修改成B,再修改回成A。当前线程的CAS操作无法分辨当前V值是否发生过变化。
      如何解决ABA问题? 通常是使用版本戳来解决这个问题,避免并发中的问题。 CAS是一种乐观锁技术

    • 重排序/happens before 
      JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。
      happen-before原则是JMM中非常重要的原则,它是判断数据是否存在竞争、线程是否安全的主要依据,保证了多线程环境下的可见性。
      happen-before原则解决了重排序带来的多线程运行问题。
      如果两个操作之间具有happens-before 关系,那么前一个操作的结果就会对后面一个操作可见。

      happens-before原则规则:

      1. 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
      2. 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
      3. volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
      4. 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
      5. 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
      6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
      7. 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
      8. 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
    • 线程池
      Java通过Executors提供四种线程池,分别为:
      (缓存、固定、定时、单例)

      1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
      2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
      3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。(定时任务建议用这个,不要用TimerTask,因为:异常退出所有任务)
      4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    • 参考文献
      文献1-并发多线程
      文献2-并发编程指南
      文献3-AQS
      文献4-concurrentHashMap

posted @ 2017-08-30 12:05  秋风悲画扇  阅读(332)  评论(0编辑  收藏  举报