JAVA并发
1.为什么使用并发,什么是并发
●可以提升多核CPU的利用率:一般来说一个主机会有多个CPU核心,我们可以创建多个线程,将不同的线程交给不同的核心去执行,这样就可以提升CPU的使用率。
●方便进行业务拆分,提升应用性能
●比如我们常见的数据库连接池,就是多线程的应用
●缺陷:
■线程也是程序需要占用内存,线程越多,消耗内存越多
■多线程需要协调管理(上下文切换),还会浪费一部分CPU性能
■线程之间对共享资源的访问会相互影响,会造成线程不安全现象。
2.并发编程的三个要素
●原子性:不可再分,要么不做,要么全做,(JAVA通过synchronized或锁来解决)
●可见性:一个线程对共享变量的修改,另一个线程能立刻看到(我觉得是通过Voliate关键字解决的)
●有序性:程序执行的顺序按照代码的先后顺序执行(处理器可能会对指令重排序,通过Happens-Before规则解决)
3.并发和并行有什么区别
●并发:多个任务在同一个核上,按细分的时间片轮流执行,由于切换很快,所以看起来像是同时执行。
●并行:单位时间内,多个处理器或多核处理器同时处理多个任务,是真正意义上的同时进行。
●串行:有n个任务,由一个线程按顺序执行。
4.进程线程和协程
●进程:一个内存中运行的应用程序,每个在运行的程序都是一个进程,是操作系统资源分配的基本单位,每个进程都有独立的代码和数据空间(程序上下文),程序切换会有较大开销。
●线程:进程中的一个任务,在程序中独立运行,是处理器任务调度和执行的基本单位,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,切换开销较小。
●协程:由于线程切换涉及到上下文切换,导致状态的切换都非常消耗性能,所以出现了一种比线程更加轻量级的存在,一个进程可以有多个协程,区别是协程不是被OS内核管理而是由程序控制(在用户态,所以不像线程切换那样消耗资源)
■Java没有实现,Python通过关键字yield实现了(Java的yield方法是让线程回到就绪队列)。下面程序中创建了一个consumer的协程,在协程中消费数据,当协程运行到yield关键字的时候,会暂停于那行,等主线程用send方法发送了数据,协程才会接收到数据,继续执行(所以是由程序控制)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BULYyVtL-1650425957586)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/119.png)]
●一个进程至少有一个线程,可以有多个线程,线程之间共享很多数据(详细见JVM)
●一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃后,可能导致整个进程都死掉
●每个进程有独立的程序运行出入口,但是线程不能独立执行,必须依存于应用程序中。
5.什么是上下文切换
●多线程编程中一般线程数多于CPU核心数,而一个核心同一时刻只能被一个线程使用,所以就按时间片轮转,当一个线程时间片用完就会先保存自己状态,然后切换到另一个任务,这个过程就是上下文切换。
●Linux与其他操作系统下那个比有个优点就是上下文切换和模式切换的时间消耗少。
6.守护线程和用户线程的区别
●用户线程(User):运行在前台,执行具体的任务。eg:程序主线程
●守护线程(Daemon):运行在后台,为其他前台线程服务,一旦用户线程结束,守护线程随着JVM一起结束。eg:GC
7.线程死锁
●线程死锁是指两个或两个以上的进程(线程),在竞争公共资源的过程中行程单一种阻塞现象,如无外力作用,将无法推进下去。
●形成死锁的必要条件:
■互斥:同一资源在某一时间段内只能被一个进程占用。
■不可剥夺:别人拥有的资源,不能剥夺下来
■循环等待:若干进程相互等待对方释放资源
■保持等待:进程有一定的资源,但不够运行,也不会主动放弃资源,一直在等待。
●避免死锁:使用定时锁,lock,tryLock来代替内部锁机制。
8.JAVA中常见的锁
●同步锁:同一时刻,一个同步锁只能被一个线程访问,以对象为依据,在Java中任何对象都可以做为锁,通过synchronized关键字进行同步,实现对竞争资源的互斥访问。
●独占锁(可重入的互斥锁):在同一个时间点,只能被一个线程持有;可重入,即可被单个线程多次获取,根据锁的获取机制又可分为公平锁和非公平锁,Java中通过ReentrantLock实现独占锁,默认为非公平锁。
■公平锁:按照自旋时间即先来先得的规则,线程依次排队,公平获取锁。Java中,ReetrantLock中有一个Sync类型的成员变量sync,它的实例为FairSync类型的时候,ReetrantLock为公平锁。设置sync为FairSync类型,只需要Lock lock = new ReetrantLock(true)
■非公平锁:当线程要获取锁的时候,会无视自旋时间等待队列而直接获取锁,如果获取不到再进入等待队列,ReetrantLock默认为非公平锁,或Lock lock = new ReetrantLock(false)
●共享锁:我们常说的读锁就是共享锁,可以被多个线程获取,而写锁就是独占锁,只能被一个线程获取。
●Java中,读写锁为ReadWriteLock接口定义&