一、进程&线程
- 程序
 是为完成特定的任务或需求,而用某种语言编写的一组指令的集合
- 进程
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
3. 线程  
进程的一个执行路径,共享一块内存,进程至少有一个线程
并发执行,是进程的进一步划分
4. 线程调度  
分时调度  
轮流使用cpu,平均分配cpu使用率
抢占式调度  
让优先级更高的先使用cpu,优先级相同的随机分配
java默认抢占式调度
java的调度方法  
同优先级线程组成先进先出队列(先到先服务),使用时间片策略
对高优先级,使用优先调度的抢占式策略
5. 线程优先级  
**MAX_PRIORITY:10
NORM_PRIORITY:5
MIN_PRIORITY:1**
- 方法
getPriority|返回此线程的优先级|
setPriority(int newPriority)|更改此线程的优先级,默认5,1~10
etDaemon|守护线程,全为守护线程JVM退出|
对于CPU的一个核来说,同一时刻只能运行一个线程,多个线程轮流切换非常快。我们看做是同一时刻多个任务执行
多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。
- 线程分类
守护线程  
用来服务用户线程的
通过在start方法前调用thread.setDaemon(true)可以把一个用户线程变成守护线程
若JVM都是守护线程,当前JVM将退出
JVM垃圾回收就是典型的守护线程
用户线程
7. 同步&异步
| 1 | 复制代码 同步 排队执行,效率低 | 
- 并发&并行
| 1 | markdown复制代码 并发 | 
二、线程的创建方式
1.Thread类
概述
- java.lang.Thread
- 每个线程都是通过Thread对象的run()方法完成操作,run()方法体称为:线程体。
- 通过该Thread对象的start()方法来启动这个线程,而非直接调用run方法。
- 一个线程对象只能调用一次start,如果重复调用,抛出lllegalThreadStateException异常
构造
- Thread():创建新的对象
- Thread(String threadName):创建线程并指定线程实例名
- Thread(Runnable target):指定线程的目标对象,它实现了runnable接口的run方法
- Thread(Runnable target,String name):创建新的Thread对象
方法
- start
 启动线程
- getName
 获得线程名
- setName
 设置线程名
- currentThread
 返回当前线程
- run
 真正的线程任务的方法
- setDaemon()
 参数为true,设置该线程为守护线程
 必须在线程启动之前,否则报IllegalThreadStateException
- yield
 线程让步
 暂停当前正在执行的线程,把机会让给优先级相同或者更高的线程
 若队列中没有同优先级的线程,忽略此方法
- join
 线程阻塞,加入线程运行
 当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止
 低优先级的线程也可以获得执行
- sleep
 线程休眠
 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队
 抛出InterruptedException异常
- isAlive
 判断线程是否存活
- interrupt
 中断线程
- interrupted
 测试当前线程是否已被中断
创建线程方式
创建 继承Thread类的类的对象,该类重写run方法
| 1 | bash复制代码 A a = new A(); | 
2.Runnable接口
创建方式
类实现Runnable接口,重写run方法,创建该类得到对象任务,new Thread(类对象)来得到线程对象
好处
- 通过创建任务,然后线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况
- 可以避免单继承所带来的局限
- 任务与线程本身是分离的,提高了程序的健壮性
- 后续学习的线程池技术,接收Runnable类型的任务,不接受Thread类的线程
3.Callable接口
创建方式
- 编写类实现Callable接口 , 实现call方法
| 1 | java复制代码class XXX implements Callable<T> { | 
- 创建FutureTask对象 , 并传入第一步编写的Callable类对象
| 1 | java复制代码FutureTask<Integer> future = new FutureTask<>(callable); | 
- 通过Thread,启动线程
| 1 | java复制代码new Thread(future).start(); | 
FutureTask对象
| 1 | arduino复制代码get | 
4.Runnable与Callable
接口定义
| 1 | csharp复制代码接口定义 | 
相同点
| 1 | scss复制代码都是接口 | 
不同点
| 1 | scss复制代码Runnable没有返回值;Callable可以返回执行结果 | 
获取返回值
| 1 | csharp复制代码Callalble接口支持返回执行结果,需要调用FutureTask.get()得到, | 
5.线程池
Executors一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作
三、线程安全&同步
1.同步
同步锁机制
| 1 | markdown复制代码 在《Thinking in java》中,是这么说的:对于并发工作, | 
注意
| 1 | kotlin复制代码 必须确保使用同一资源的多个线程共用一把锁,非常重要。否则无法保证共享资源 | 
同步的范围
| 1 | 复制代码1.明确哪些是多线程执行的代码 | 
所有操作共享数据的这些语句都要放在同步范围中
释放锁操作
| 1 | bash复制代码1.当前线程的同步方法、同步代码块执行结束 | 
不会释放锁操作
| 1 | scss复制代码1.线程执行同步代码块或同步方法时,调用Thread.sleep()\Thread.yield()方法暂停当前线程执行 | 
应尽量避免使用suspend和resume来控制线程
2.synchronized
- 任何对象 都可作为锁对象,同一任务含有同一锁对象才会保证线程同步
 同步代码块
- synchronized(obj){ }
- 锁自己指定,可以使this或者className.class
 同步方法
- public synchronized void test(){ }
- 静态方法的锁是 className.class
- 非静态方法锁是this
3.死锁
| 1 | 复制代码不同线程分别占用对方需要同步的资源,不放弃,都在等待对方让步,形成线程死锁 | 
解决
- 专门的算法、原则
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
4.Lock锁
JDK5.0开始,加入通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当
- java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
- 锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
- ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义。比较常用ReentrantLock,可以显示加锁,释放锁
- 格式
| 1 | java复制代码 //加锁 | 
5.synchronized与Lock锁比较
- Lock是显示锁(手动开启和释放锁),synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块和方法锁
- 使用Lock锁,JVM将花费较少时间来调度来调度线程,性能更好。并具有良好扩展性(提供更多的子类)
- 优先使用顺序
- 同步代码块(已经进入方法体,分配了相应资源)->同步方法(在方法体外)*
6.线程通信
- wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程等待其他线程调用notify和notifyAll方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行
- notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待。
- noyifyAll():唤醒正在排队等候资源的所有线程结束等待注意
 这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则报java.lang.lllegalMonitorstateException异常
 因为这三个方法必须有锁对象调用,而任意对象都可作为synchronized的同步锁,因此这三个方法只能声明在Object类中
四、线程池
1.概述
- 背景
 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间
- Executors一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作
2.好处
- 降低资源消耗。
- 提高响应速度。
- 提高线程的可管理性
3.四种线程池
(1)缓存线程池:newCachedThreadPool
- ExecutorService service = Executors.newCachedThreadPool();
- (长度无限制)
- 过程:- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在,则创建线程 并放入线程池, 然后使用
 
(2)定长线程池:newFixedThreadPoll
- ExecutorService service = Executors.newFixedThreadPool()
- (长度固定)
- 过程:- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
 
- 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
(3)单线程线程池:newSingleThreadExecutor
- ExecutorService service = Executors.newSingleThreadExecutor()
- 1个
- 过程:- 判断线程池 的那个线程 是否空闲
- 空闲则使用
- 不空闲,则等待 池中的单个线程空闲后 使用
 
(4)周期性任务定长线程池:newScheduledThreadPool
- ScheduledExecutorService service = Executors.newScheduledThreadPool(corePoolSize); 
- 周期任务 定长线程池 
- 过程 - 判断线程池是否存在空闲线程
- 存在则使用
- 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
- 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
 
- 周期性任务执行 - 定时任务 - schedule(Callable callable,long delay, TimeUnit unit); - 参数1command. runnable类型的任务 - 参数2delay. 时长数字 - 参数3unit. 时长数字的单位 - 周期性任务 - scheduleAtFixedRate(Runnable command,long initialDelay, long period, TimeUnit unit); - 参数1command. runnable类型的任务 - 参数2initialDelay. 时长数字(延迟执行的时长) - 参数3period. 周期时长(每次执行的间隔时间) - 参数4unit. 时长数字的单位 
 
 
- 注意生成对象为ScheduledExecutorService 
4.常用参数
- corePoolSize:核心池的大小、
- maximumPoolSize:最大线程池数
- keepAliveTime:无任务时线程最多保持时间会终止
5.相关API
ExecutorService和Excutors
ExecutorService: 真正线程池接口。常见子类:ThreadPoolEcecutor
Excutors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
有四种线程池:newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newSingleThreadPool
本文转载自: 掘金