线程回顾
线程基本概念
程序: 静态的代码
进程: 运行中的程序,被加载到内存中,是操作系统分配内存的基本单位
线程: 线程是程序处理的基本最小单位 ,是cpu执行的单元
线程的创建方式:
类 继承 Thread 重写run() 创建类的对象
实现Runnable接口 重写run() 任务 new Thread(任务)
实现Callable接口 重写call() 有返回值,可以抛出异常
线程的状态
1. 新建(NEW):新创建了一个线程对象。
2. 就绪:线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
3. 运行:可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
4. 阻塞:阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu,暂时停止运行。直到线程进入可运行状态,才有机会再次获得cpue 转到运行)状态。
5. 死亡:线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生
线程中的常用方法
getName(); 返回线程的称
start(); 启动线程
setName() ;设置线程的名称
setPriority(); 设置线程的优先级 (并不是优先级高的线程一定先执行 要由CUP的调度决定)
线程优先级最大为10,最小为1,默认为5
join() ; 等待线程终止
currentThread() ;返回对当前正在执行的线程对象的引用
sleep(long millis) 让当前正在执行的线程休眠(暂停执行)
yield() ;线程让步 让出CPU的执行权 进入就绪队列,等待CPU的调用
isAlive(); 测试线程是否在进行;
wait此方法会使当前线程就进入阻塞状态,并释放同步监视器。
notify此方法会唤醒被wait的一个线程。如果有多个线程被wait, 就唤醒优先级高的那个。
notifyAll一旦执行此方法,就会唤醒所有被wait的线程
多线程
多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。比如程序实现一些需要等待的任务时,比如用户输入,文件读写操作,网络操作,搜索等。
多线程的优点: 提高程序的响应,提高CPU利用率,改善程序结构,将复杂任务分为多个线程,独立运行。
多线程的缺点:线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;多线程需要协调和管理,所以需要cpu时间跟踪线程;线程之间对共享资源的访问会相互影响。
并发编程
并发与并行
并发 说的是在 一个时间段内 ,多件事情在这个时间段内 交替执行 。并行 说的是多件事情在 同一个时刻 同事发生。
java内存模型(JMM)
java内存模型,是java虚拟机规范的一种工作模式.将内存分为主内存和工作内存.变量数据存储在主内存中,线程在操作变量时,会将主内存中的数据复制一份到工作内存.在工作内存中操作完成后,再写回到主内存中.
并发编程的核心问题
1.不可见性
那么如何解决以上问题呢:
volatile关键字
volatile底层实现原理
有序性问题:volatile 修饰的变量在操作前,添加内存屏障,禁止指令对其进行重排序.
可见性问题:volatile通过 Lock 前缀指令 + MESI 缓存一致性协议来实现可见性。JVM 会发送一个 Lock 前缀指令给 CPU,CPU 在执行完写操作后,会立即将新值刷新到内存,同时因为 MESI 缓 存一致性协议,其他各个 CPU 都会对总线查看,看自己本地缓存中的数据是否被别人修改,如果发现修改了,会把自己本地缓存的数据丢弃掉。然后这个 CPU 里的线程在使用变量时,就会从主内存里加载最新的值了,这样就保证了可见性
原子性问题
volatile关键字可以解决可见性和有序性问题 但无法解决由线程切换带来的原子性问题,那么原子性问题该怎样解决呢?
synchronized是独占锁/排他锁(就是有你没我的意思),synchronized并不能阻止CPU时间片的切换,只是当其他线程要访问这个资源时,发现锁还未释放,所以只能在外面等待。synchronized一定能保证原子性,因为被synchronized修饰某段代码后,无论是单核CPU还是多核CPU,只有一个线程能够执行该代码,所以一定能保证原子操作。
除了加锁我们还可以使用原子变量解决原子性问题。
原子变量最主要的一个特点就是所有的操作都是原子的,synchronized关键字也可以做到对变量的原子操作。只是synchronized的成本相对较高,需要获取锁对象,释放锁对象,如果不能获取到锁,还需要阻塞在阻塞队列上进行等待。而如果单单只是为了解决对变量的原子操作,建议使用原子变量。
原子类
在java中还提供一些原子类,是一种无锁实现.采用CAS机制(CAS(Compare-And-Swap) 比较并交换)是一种无锁实现.在低并发情况下使用.
CAS
CAS是一种机制 是乐观锁的一种实现方式,他采用的是自旋锁的思想,是一种轻量级的锁机制。 即每次判断我的预期值和内存中的值是不是相同,如果不相同则说明该内存值 已经被其他线程更新过了,因此需要拿到该最新值作为预期值,重新判断。而该线程不断的循环判断是否该内存值已经被其他线程更新过了,这就是自旋的思想