并发的背后故事:
并发:程序同时处理多个任务的能力。
并发编程的根源在于对多任务情况下对资源访问的有效控制。
程序、进程、线程:
程序是静态的概念,windows下通常指exe文件,java中程序是一个war包或者jar包。
进程是动态的概念,是程序在运行状态,进程说明程序在内存中的边界。
线程是进程内的一个基本任务,每个线程都有自己的功能,是CPU分配与调度的基本单元。
并发与并行:
并行:多核CPU
并发:CPU时间片:同一时间只有一个线程在运行。
同步和异步:
同步:
异步:
临界区:
临界区用来表示一种公共资源与共享数据,可以被多个线程使用。
同一时间只能有一个线程访问临界区(阻塞状态),其他资源必须等待。
死锁、饥饿、活锁:
线程安全:
在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全的三大特性:
原子性:
即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行。i=i+1(不是原子性)
可见性:
当多个线程访问一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改后的值。
有序性:如果在当前线程内观察,所有的操作都是有序的,如果在一个线程观察另一个线程,所有的线程都是无序的。
java内存模型:
java memory model
栈:Stack
每个线程创建一个栈
存储执行方法的执行信息
线程私有,无法共享
先进后出,后进先出
连续存储,执行效率高
堆:Heap
用于存储对象
JVM全局唯一
堆不是连续的
执行效率低
所有线程共享
方法区:静态区
类结构信息
静态变量static
静态方法
存储内容不变
存储字符串
java中创建线程的三种方式:
继承Thtread类
实现Runable接口
使用Callable和Future
创建线程的三种方式对比:
继承Thtread类 优点:
多线程的同步机制:
synchronized(同步锁)关键字的作用就是利用一个特定的对象设置一个锁lock(绣球),在多线程(游客)并发访问的时候,同时只允许一个线程(游客)可以获得这个锁,执行特定的代码(迎娶新娘)。执行后释放锁,继续由其他线程争抢。
synchornized的使用场景:
synchornized代码块,对于任意对象
synchornized方法,对于this当前对象
synchornized静态方法,对于该类的字节码对象
线程的五种状态:
新建new
就绪ready
运行中runing
阻塞blocked
死亡:dead
死锁产生的原因:
死锁是在多线程情况下最严重的问题,在多线程对公共资源(文件、数据)等进行操作时,彼此不释放自己所占用的公共资源,而去试图操作其他线程的资源,而形成交叉引用,就会产生死锁。
解决死锁最根本的建议:
尽量减少对公共资源的引用,用完马上释放
减少使用synchd,采用副本方式替代。
线程(不)安全的类:
线程安全:Vector Properties StringBuffer HashTable
线程不安全:ArrayList/LinkedList HashSet/TreeSet StringBuilder HashMap
java.util.concurrent
并发伴随着多核处理器的诞生而产生的,为了充分利用硬件资源,诞生了多线程技术。但是多线程又存在资源竞争的问题,引发了同步和互斥的问题,JDK1.5推出的java.util.concurrent并发工具包来解决这些问题。
ThreadPool线程池
重用存在的线程,减少对象、消亡的开销
线程总数可控,提高资源的利用率
避免过多资源竞争,避免阻塞
提高额外功能、定时执行、定期执行、监控等。
new Thread的弊端
新建对象,性能差
线程缺乏统一管理,可能无限制的创建线程,相互竞争,严重时会占用过多的系统资源导致宕机或OOM内存溢出。
线程池的种类:
CachedThreadPool:可缓存线程池
FixedThreadPool:定长线程池
SingleThreadPool:单线程池
ScheduledThreadPool:调度线程池
CountDownLatch-倒计时锁
CountDownLatch-倒计时锁特别适合“总-分任务”例如多线程计算后的数据汇总
CountDownLatch类位于java.uitl.concurrent包下,利用它可以实现类似计数器的功能。
Semphore信号量
对不起服务器已满,请稍后再试!
重入锁:指任意线程在获取到锁之后,再次获取该锁而不会被该锁所阻塞
ReentrantLock设计目的是用来代替synchronized关键字