16.1线程概述
    Process
    Thread
    进程:系统进行资源分配和调度的独立单位
    独立性 动态性 并发性
    线程是进程的执行单元
    主线程
    一个线程必须有一个父进程
    线程的执行是抢占式的
    进程间的线程,共享内存、文件句柄等
    使用多线程,比使用多进程,实现并发的性能高的多
    Java虚拟机在后台内置了一个超级线程来进行垃圾回收
    16.2 线程的创建和启动
    两种方法:
    1继承Thread类:创建的子类,可以直接代表线程对象
    this 来获取当前对象
    2实现Runnable接口:只能作为线程对象的target
    Thread.currentThread() 静态方法,获取当前对象
    run()方法,线程执行体
    Thread(Runnable target)

    1. 分配新的 Thread 对象

    Thread(Runnable target, String name)

    1. 分配新的 Thread 对象。

    3使用Callable和Furture创建线程
    使用Callable和Furture创建线程
    Callable接口中提供了一个call() 方法,才可以作为线程执行体,类似于run()。
    但是有返回值,可以抛出异常
    因此Callable完全可以作为Thread的一个target
    但是Callable接口不是Runnable的子接口,call()还有返回值,怎样才能拿到call方法的返回值呢?
    —>Furture接口代表该返回值
    —>其实现类 :FurtureTask类
    FurtureTask实现了Furture和Runnable接口
    使用步骤:
    1、创建Callable类,实现call()方法—>创建Callable对象
    2、使用FurtureTask来包装Callable类
    3、new Thread(furtureTask,“新线程”).start
    4、get方法可以获取call()的返回值
    一般推荐用Runnable接口或Callable接口来创建多线程:
    优点:
    支持多线程共享一个Target对象
    还可以继承其他类
    Thread.currentThread()
    继承Thread:简单,this即可获取当前线程对象
    但是不能再继承了其他父类了
    16.3 线程的生命周期 P725 一共5个状态 新建 就绪 运行 阻塞 死亡
    1、新建 new对象
    2、就绪 new ThreadTwo().start() 调用start()以后
    启动线程 用start(),
    不能用run(),用run()会被当成一个普通对象,而不是一个线程执行体
    run()程序执行过程中,其他线程无法并发执行
    就绪后,等待执行,不会立即执行
    Thread.sleep(1) 线程休息1ms 进入阻塞状态
    3、运行
    抢占式调度策略—所有现代桌面和服务器OS
    协作式策略—某些手机OS;需要主动放弃所占用的CPU资源
    4、阻塞
    进入阻塞的几种情况:
    sleep()
    IO阻塞
    等待同步锁
    等待通知
    suspend() 尽量少用
    阻塞后只能进入就绪状态
    resume() —>返回就绪状态
    线程调度器
    5、死亡
    isAlive()
    true 就绪、运行、阻塞
    false 新建/死亡
    start() 方法 只有新建状态的线程才能使用
    16.4 控制线程的方法
    join线程 让一个线程阻塞,等待另一个线程完成
    x.join() 在主线程中调用这个方法,将等该x线程执行完成,然后在回到主线程。再次之前主线程阻塞。
    后台线程/守护线程 Daemon Thread
    垃圾回收机制,是典型的后台线程
    当所有前台线程死亡时,后台线程自动死亡
    前台进程的子线程默认前台进程,后台进程一样
    setDaemon(true) 必须在进程start之前使用
    线程睡眠
    sleep()
    会阻塞进程,进入到阻塞状态。sleep()结束后,又会回到就绪状态
    线程让步
    yield()
    不会阻塞进程,会让当前进程进入到就绪状态,让系统的线程调度去重新调度一次
    完全可能的情况是:刚刚yield完成,线程调度器立马就将其调度出来重新执行
    能否被调度出来,与该线程的优先级有关
    sleep()方法比yield()有更好的移植性
    改变线程优先级
    每个线程的默认优先级与创建它的父线程优先级相同
    void setPriority(int newPriority)

    1. 更改线程的优先级

    16.5 线程同步P731
    同步监视器
    同步代码块synchronized Block
    任何时刻只有一个线程可以获得对同步监视器的锁定,
    同步代码块执行完毕后,该线程会释放对同步监视器的锁定
    推荐使用可能被并发访问的共享资源充当同步监视器
    放在run方法里面,对一片代码块进行修饰
    synchronized (account){

    执行该代码之前,一定要先获取到同步监视器的锁定
    同步监视器,同时只有一个线程能获取到
    }
    同步方法synchronized Method
    线程安全的类具有如下特征:

    • 该类的对象可以被多个线程安全的访问;
    • 每个线程调用该对象的任意方法之后都将得到正确的结果;
    • 每个线程调用该对象的任意方法后,该对象状态依然保持合理状态

    不可变类总是线程安全的,因为它的对象状态不可改变,但可变对象需要额外的方法来保证其线程安全。
    synchronized 关键词 修饰方法
    同步监视器是this,即调用该方法的对象
    synchronized的用法(Java中每一个对象都可以作为锁,这是synchronized实现同步的基础)
    1、普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
    2、静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
    3、同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
    加锁-修改-释放锁
    面向对象中流行的设计方法:DDD,即Domain Driven Design 领域驱动设计
    可变类的线程安全是以降低程序的运行效率为代价的,可采用如下策略减少线程安全所带来的负面影响:

    • 不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法进行同步;
    • 如果可变类有两种运行环境:单线程和多线程,则应该为该可变类提供两种版本,线程安全与线程不安全版本

    (如StringBuilder保证性能,StringBuffer保证线程安全)
    同步监视器,一般是可能被并发访问的共享资源
    如下情况,线程不会释放同步监视器:

    • 线程执行同步代码块或同步方法时,程序调用sleep、yield方法来暂停当前线程的执行,当前线程不会释放同步监视器;
    • 线程执行同步代码快时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。

    尽量避免使用suspend和resume方法来控制线程
    同步锁Lock
    某些锁允许对共享资源并发访问,如ReadWriteLock
    Lock、ReadWriteLock是Java5新提供大的两个根接口;
    为Lock提供了ReentrantLock(可重入锁)实现类;ReentrantLock较常用
    为ReadWriteLock提供了ReentrantReadWriteLock实现类
    ReentrantLock锁具有可重入性,一个线程可以对已被加锁的ReentrantLock锁再次加锁
    同步代码块/同步方法: 隐式使用当前对象作为同步监视器 更好用 简单
    锁Lock: 显式使用Lock对象作为同步锁 更灵活
    同步代码块,一般写在线程Thread里面的run()方法里面,synchronized(Obj) {}
    同步方法,写在对象中,synchronized 作为关键词,修饰方法 DDD
    Lock是定义在对象里面的,使用在线程安全的方法里面
    ReentrantLock具有可重入性,即可以对已经加锁的ReentrantLock重新加锁
    死锁dead lock:JVM无法识别,不处理,所有线程处于阻塞状态,无法继续
    锁等待
    http://blog.csdn.net/qq_20641565/article/details/53208909
    Lock、synchronized和ReadWriteLock的区别和联系
    Lock和synchronized最大的区别就是当使用synchronized,一个线程抢占到锁资源,其他线程必须像SB一样得等待;
    而使用Lock,一个线程抢占到锁资源,其他的线程可以不等待或者设置等待时间,实在抢不到可以去做其他的业务逻辑(tryLock()方法)

    • ReadWriteLock

    Java.util.concurrent.locks.ReadWriteLock有一种高级的线程锁机制,它允许多个线程读某个资源,但每次只允许一个线程来写
    ReadWriteLock Locking规则
    下面是一个线程允许锁住ReadWriteLock然后对保护的资源进行读或写操遵循的原则

    • Read Lock

    如果没有写入线程锁住ReadWriteLock,并且没有线程需要获得写入锁进行写入操作。那么多个线程可以获得锁来进行读操作。

    • Write Lock

    如果没有线程在写或者读操作,那么一次仅有一个线程可以获得锁以进行写操作。
    16.6线程通信
    1、传统的线程通信 使用同步方法
    这些方法与进程的5个状态无关,不会改变进程状态?
    wait方法,导致线程等待 —> 调用wait方法当前线程会释放对于该同步监视器的锁定;
    notify唤醒在此同步监视器上等待的单个线程,任意选择一个唤醒,只有当前线程放弃对该同步监视器的锁定后,才可以执行被唤醒的线程;
    notifyAll唤醒在此同步监视器上等待的所有线程。只有当前线程放弃对该同步监视器的锁定后,才可以执行被唤醒的线程。
    这三个方法属于Object类,只能由同步监视器对象来调用
    sleep是线程类(Thread)的方法,执行此方法会导致当前此线程暂停指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法或notifyAll后本线程才获得对象锁进入运行状态
    2、使用Condition控制线程通信 使用Lock
    只有使用synchonized关键字的情况下,才能用传统的线程通信方式 wait notify notify
    若使用Lock对象来实现同步,那么就需要使用Condition控制线程通信
    lock.newCondition
    await:类似于隐式同步监视器上的wait方法,导致当前线程等待,直到其他线程调用该Condition的signal方法或signalAll方法来唤醒该线程,await方法有更多变体。。。
    signal:唤醒在此Lock对象上等待的单个线程。如果所有线程都在该Lock对象上等待,则会选择唤醒其中一个线程。选择是任意性的。只有当前线程使用await方法放弃对该Lock对象的锁定后,才可以执行被唤醒的线程。
    singalAll:唤醒在此Lock对象上等待的所有线程,只有当前线程使用await方法放弃对该Lock对象的锁定后,才可以执行被唤醒的线程。
    3、使用阻塞队列控制线程通信 BlockingQueue接口
    BlockingQueue是Queue的子接口,作为线程同步的工具
    当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;
    当消费者线程试图从BlockingQueue中取出元素时,如果该队列已空,则该线程被阻塞

    1. 原来格式为表格(table),转换较复杂,未转换,需要手动复制一下
    2. {"cells":[{},{"value":"抛出异常"},{"value":"特殊值"},{"value":"阻塞","inlineStyles":{"bold":[{"from":0,"to":2,"value":true}]}},{"value":"超时"},{"value":"插入"},{"value":"add(e)"},{"value":"offer(e)"},{"value":"put(e)","inlineStyles":{"bold":[{"from":0,"to":6,"value":true}]}},{"value":"offer(e, time, unit)"},{"value":"移除"},{"value":"remove()"},{"value":"poll()"},{"value":"take()","inlineStyles":{"bold":[{"from":0,"to":6,"value":true}]}},{"value":"poll(time, unit)"},{"value":"检查"},{"value":"element()"},{"value":"peek()"},{"value":"不可用","inlineStyles":{"bold":[{"from":0,"to":3,"value":true}]}},{"value":"不可用"}],"heights":[23,23,23,23],"widths":[70,70,70,70,117]}

    16.7线程组和未处理的异常
    ThreadGroup 线程组
    setUncaughtExceptionHandlers
    16.8 线程池
    http://www.importnew.com/19011.html
    深入理解Java之线程池
    http://ifeve.com/java-threadpool/
    聊聊并发(三)Java线程池的分析和使用
    第16章 多线程 - 图1
    new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
    keepAliveTime, milliseconds,runnableTaskQueue, threadFactory,handler);
    该线程池正常维护的线程池大小就是corePoolSize
    除非线程数达到corePoolSize且任务队列也满了,才会继续增加线程数
    最后线程池的线程数也有一个最大限度maximumPoolSize
    如果maximumPoolSize已经超出且任务队列已经满了,此时将报错,无法处理了
    CPU密集型任务配置尽可能少的线程数量,如配置Ncpu+1个线程的线程池。
    IO密集型任务则由于需要等待IO操作,线程并不是一直在执行任务,则配置尽可能多的线程,如2*N cpu。
    16.9 线程相关类 P757
    1ThreadLocal
    该类提供了线程局部 (thread-local) 变量
    ThreadLocal和其他同步机制一样,也是为了解决多进程对同一变量的访问冲突
    2包装线程不安全的集合 ArrayList HashSet等
    3线程安全的集合类
    Concurrent开头的集合类
    CopyOnwrite开头的集合类
    第16章 多线程 - 图2
    Concurrent开头的集合类 支持多个线程并发写入访问
    ConcurrentHashMap 支持16个线程并发写入,超过16,有一些线程需要等待
    可修改构造函数的concurrencyLevel参数,其默认值是16。
    CopyOnWriteArrayList
    写入:每次均复制一个新数组操作
    适合读取操作远多于写入的场景,如缓存
    Java 并发实践 — ConcurrentHashMap 与 CAS
    http://www.importnew.com/26035.html
    CAS:Compare And Swap
    深入并发包 ConcurrentHashMap
    http://www.importnew.com/26049.html !!!
    HashMap 容易出现闭环,get方法容易出现死循环;所以HashMap是线程不安全的。
    HashTable 它是线程安全的,它在所有涉及到多线程操作的都加上了synchronized关键字来锁住整个table,这就意味着所有的线程都在竞争一把锁,在多线程的环境下,它是安全的,但是无疑是效率低下的
    其实HashTable有很多的优化空间,锁住整个table这么粗暴的方法可以变相的柔和点,比如在多线程的环境下,对不同的数据集进行操作时其实根本就不需要去竞争一个锁,因为他们不同hash值,不会因为rehash造成线程不安全,所以互不影响,这就是锁分离技术,将锁的粒度降低,利用多个锁来控制多个小的table,这就是这篇文章的主角ConcurrentHashMap JDK1.7版本的核心思想
    从ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中
    在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中
    通过把整个Map分为N个Segment(类似HashTable)ConcurrentHashMap可以提供相同的线程安全,但是效率提升N倍,默认提升16倍
    HashMap与ConcurrentHashMap的区别
    人很聪明,真的很聪明。既然不能全锁(HashTable)又不能不锁(HashMap),所以就搞个部分锁,只锁部分,用到哪部分就锁哪部分。
    一个大仓库,里面有若干个隔间,每个隔间都有锁,同时只允许一个人进隔间存取东西。
    但是,在存取东西之前,需要有一个全局索引(Hash),告诉你要操作的资源在哪个隔间里,然后当你看到隔间空闲时,就可以进去存取,如果隔间正在占用,那你就得等着。聪明!!
    ConcurrentHashMap的遍历是从后向前历遍的.因为如果有另一个线程B在执行clear操作时,会把table中的所有slot都置为null,这个操作是从前向后执行
    如果线程A在遍历Map时也是从前向后,则有可能会出现追赶现象。
    Map的遍历
    Iterator> it = map.entrySet().iterator();
    30 while (it.hasNext()) {
    31 Map.Entry entry = it.next();
    32 System.out.println(“key= “ + entry.getKey() + “ and value= “ + entry.getValue());
    33 }
    http://www.importnew.com/19685.html
    Java中的几个HashMap/ConcurrentHashMap实现分析
    第16章 多线程 - 图3
    第16章 多线程 - 图4
    第16章 多线程 - 图5
    JDK8 有全新的实现: