Java提供并发主要通过两种方式:Java语言和Java类库,从5.0之后,Java又提供了高级的API:java.util.concurrent,来更好的支持并发
什么是并发?能够同时执行多项任务的能力叫并发。
进程和线程
进程和线程是两个基本的执行单位。进程是资源分配的基本单位,线程是处理机调度的基本单位
进程
进程是一个自包含的运行环境,它有一套完整的私有的资源。一个程序可由多个进程组成。,进程之间通过进程间通讯进行通信,操作系统提供进程间通讯的功能
线程
线程是轻量级的进程,创建线程需要的资源要比进程少。线程存在于进程中。
线程对象
两种实现并发程序的思路
- 当需要执行异步任务时,直接控制线程的创建和管理(程序比较简单时),采用直接new 线程的方式
- 抽象出程序的异步任务,将异步任务传递给Excutor,从而将线程的创建和管理从程序的其他部分中抽取出来(程序比较复杂时),采用线程池的方式
两种创建线程的方式
- 实现Runnable接口,实现run方法(常用)
- 继承Thread类,重写run方法(简单)
注意这两者的区别
sleep()
让当前的线程终止执行一段时间,这是线程主动发起的。
interupt()
设置线程的interupt flag.
join() 让当前线程等待被调用线程执行完毕或者等待一定的时间
同步
线程间通讯主要通过字段共享,这种通讯方式十分高效,但是会导致线程冲突和内存一致错误,同步正式防止这些错误的工具。
但是线程同步会引起线程竞争,当多个线程同时获取相同的资源时,会在这些线程间产生竞争,这会导致某个或者多个线程的执行速度减慢,甚至停止执行 。饥饿和活锁就是两种线程竞争的形式。
- 线程冲突
如果两个运行在不同线程里的操作由多个步骤组成,同时这两个操作作用于同样的数据,如果这两个操作有重叠的部分,那么这个时候线程冲突就形成了。在不同的条件下,由于重叠部分的不同,会有不同的结果,所以线程冲突的问题很难定位和解决。
2. 内存一致性错误
当两个线程对同一个数据获得的值不相同时,这时,内存一致性错误就发生了。解决的方式是,在这两个线程之间建立对这个相同数据的 happen-before关系,而建立这种关系的方式就是 同步。
3. 同步方法
当一个线程调用一个对象的时,会自动获得这个对象的内部锁,当方法返回时,会自动释放锁。当调用类的静态同步方法时,获取的是这个类的 .Class的对象的内部锁
会造成活锁
4.同步块
每一个对象都有一个内部锁,也叫监视器锁。在访问共享数据之前,线程需要获得这个内部锁,当访问结束时,释放这个锁。在没有释放锁之前,没有得到锁的线程只能阻塞。当释放内部锁时,这样就在后续访问的线程之间建立了happen-before关系
5. 可重入锁 一个线程不能获得其他线程获得的锁,但是它可以获得它已经拥有的锁
原子操作
活性问题
死锁
饥饿
活锁