启动 start方法,会自动以信进程调用run方法 直接调用run方法,将变成串行执行 同一个线程,多次start会报错,只执行第一次start方法 多个线程启动,先后顺序是随机的 线程无需关闭,只要其run方法执行结束后,自动关闭 main函数(线程)可能早于新线程结束,整个程序并不终止 整个程序终止是等所有的线程都终止(包括main函数线程)
Thread vs Runnable Thread占据了父类的名额,不如Runnable方便 Thread类实现 Runnable Runnable启动时需要Thread类的支持 Runnable更容易实现多线程中资源共享 结论:建议实现Runnable接口来完成多线程
关键步骤加锁限制 互斥:某一个线程运行一个代码段(关键区),其他线程不能同时运行这个代码 同步:多个线程的运行,必须按照某一种规定的先后顺序来运行 互斥是同步的一种特例 互斥的关键字是synchronized synchronized代码块/函数,只能一个线程进入 synchronized加大性能负担,但是使用简便
线程组ThreadGroup 线程的集合 树形结构,大线程组可以包括小线程组 可以通过enumerate方法遍历组内的线程,执行操作 能够有效管理多个线程,但是管理效率低 任务分配和执行过程高度耦合 重复创建线程,关闭线程操作,无法重用线程。
从JDK5开始提供Executor FrameWork 分离任务的创建和执行者的创建 线程重复利用(new线程的代价很大) 理解共享线程池的概念 预设好的多个Thread,可弹性增加 多次执行很多很小的任务 任务创建和执行过程解耦合 程序员无需关心线程池执行任务过程
主要类: ExecutorService,ThreadPoolExecutor,Future -Executor.newCachedThreadPool/ new FixedThreadPool来创建线程池 -ExecutorService 线程池服务 -Callable 具体的逻辑对象(线程类) -Future 返回结果
主要类: -ForkJoinPool 任务池 -RecursiveAction -RecuresiveTask 适合分配量化不了的任务
常用的数据结构是线程不安全的 -ArratList,HashMap,HashSet非同步的 -多个线程同时读写,可能会抛出异常或者数据错误 传统的Vector,Hastable等同步集合性能过差 并发数据结构:数据添加和删除 -阻塞式集合:当集合为空或者满时,等待 -非阻塞式集合:当集合为空或者满时,不等待,返回null或异常 List -Vector 同步安全,写多读少 -ArrayList 不安全 -Collection.synchronizedList 基于 synchronized,效率差 -CopyOnWriteArrayList 读多写少,基于复制机制,非阻塞 Set -HashSet 不安全 -Collection.synchronizedSet 基于synchronized,效率差 -CopyOnWriteArraySet 基于CopyOnWriteArrayList实现 ,读多写少,非阻塞 Map -HashMap 同步安全,写多读少 -HashMap 不安全 -Collection.synchronizedMap ,效率差 -ConcurrentHashMap 读多写少,非阻塞 Queue Deque -ConcurrentLinkedQueue 非阻塞 -ArrayBlockingQueue/LinkedBlockingQueue 阻塞
Lock也可以实现同步的效果 -实现更复杂的临界区结构 -tryLock方法可以预判锁是否空闲 -允许分离读写的操作,多个读,一个写 -性能更好 ReentrantLock类,可重入的互斥锁 ReentrantReadWriteLock类,可重入的读写锁 lock和unlock函数
Semaphore 信号量:本质上是一个计数器 计数器大于0 可以使用,等于0不能使用 可以设置多个并发量,例如限制10个访问 Semaphore -acquire 获取 -release 释放 比lock更近一步,可以控制多个同时访问关键区