为什么需要并行?
-业务需求
-性能
并行和并发
并行:两条线程一起跑 (多核)
并发:两条线程交替跑 (单核)
临界区
表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只有一个线程使用它,一旦临界区资源被占用,其他线程想要使用这个资源,就必须等待;
阻塞和非阻塞
阻塞和非阻塞通常用来形容多线程间的相互影响;比如一个线程占用了临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中进行等待,等待会导致线程挂起,这种情况就是阻塞;此时,如果占用资源的线程一直不愿意释放资源,那么其他阻塞在这个临界区上的线程都不能工作;
非阻塞允许多个线程同时进入临界区;
死锁、饥饿和活锁
死锁:指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程;静态问题
产生死锁的必要条件:
-互斥使用
-循环等待
-不可被剥夺
-占有且等待
饥饿:指一个或多个线程因为种种原因无法获得所需要的资源,导致一直无法执行;
活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败;
比如:线程A和B都需要过桥(都需要使用进程),而都礼让不走(那到的系统优先级相同,都认为不是自己优先级高),就这么僵持下去.
并发级别:
-阻塞 当一个线程进入临界区后,其他线程必须等待
以下三种统称非阻塞
-无障碍 无障碍是一种最弱的非阻塞调度;自由出入临界区;无竞争时,有限步内完成操作,有竞争时回滚数据;
-无锁 必须是无障碍的,保证有一个线程可以胜出
-无等待 是无锁的,要求所有的线程必须在有限步内完成,无饥饿的; (例:所有的读线程)
加速比=优化前系统耗时/优化后系统耗时
总结: 提高程序可串行化的比重和合理增加cpu
多线程基础
什么是线程?
线程是进程内的执行单元;
线程的基本操作
线程中断
Thread.interrupt()//中断线程
Thread.isInterrupted//判断是否被中断
Thread.interrupted//判断是否被中断,并清除当前中断状态
守护线程
在后台默默地完成一些系统性服务,比如垃圾回收线程、JIT线程就可以理解为守护线程
当一个java程序只有守护线程时java虚拟机就会自动退出;
线程优先级
设置优先级setPriority()
基本的线程同步操作
synchronized
指定加锁对象:给定对象加锁,进入同步代码块前要获得给定对象的锁
直接作用于实例方法:相当于对当前实例加锁,进入同步代码块前要获得当前实例的锁
直接作用于静态方法:相当于对当前类加锁,进入同步代码块前要获得当前类的锁
Object.wait() Object.notify()
Java内存模型和线程安全
原子性
原子性是指一个操作是不可中断的。即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰;
i++是原子操作么? 不是,读取i的值、++、写回;
有序性
即程序执行的顺序按照代码的先后顺序执行。
处理器有指令重排;
可见性
可见性是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改;
-编译器优化
-硬件优化(如写吸收,批操作)
Happen-Before规则
程序顺序原则:一个线程内保证语义的串行性 ; 保证重排之后的结果与重排之前保持一致; a=1;b=a+1;
volatile规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性;
锁规则:解锁(unlock)必然发生在随后的加锁(lock)前;
传递性:A先于B,B先于C,那么A必然先于C
线程的start()方法先于它的每一个动作;
线程的所有操作都先于线程的终结(Thread.join);
线程的中断(interrupt())先于被中断线程的代码
对象的构造函数执行结束先于finalize()方法;