Conditon接口
-Lock接口的方法newCondition()获取
- public Condition newCondition()
-常用方法
- public void await() 线程等待
- public void signal() 唤醒一个等待的线程
- public void signalAll() 唤醒所有等待的线程
-Condition接口方法和Object类方法比较
- Condition可以和任意的Lock组合,也就是实现了线程的分组管理
- 一个线程的案例中,可以使用多个Lock锁,每个Lock锁上可以结合Condition对象
- synchronized同步中做不到线程分组管理
- Object类wait()和notify()都要和操作系统交互,并通知CPU挂起线程,唤醒线程,效率低
- Condition接口方法await()不和操作系统交互,而是让线程释放锁,并存放到线程队列容器中,当被Signal()唤醒后,从队列中出来.从新获取锁后在执行
- 因此使用Lock和Condition的效率比Object要快很多
java并发编程的三大特性
-原子性
- 定义
- 原子性,即一个操作或多个操作,要么全部执行并且在执行的过程中不被打断,要么全部不执行(提供了互斥访问,在同一时刻只有一个线程进行访问)
- java提供了原子性的技术保障有
- synchronized(互斥锁)
- Lock(互斥锁)
- 原子类(CAS)
上面两个都是通过互斥锁实现,即同一时刻只允许一个线程操作该变量,保障了原子性
- 原子类Atomiclnteger java.util.concurrent.atomic.AtomicInteger
- 构造方法
- public AtomicInteger()创建具有初始值0的新AtomicInteger.
- public AtomicInteger(int initialvalue)创建具有给定初始值的新AtomicInteger
- 方法
- int incrementAndGet()以原子方式将当前值加1
- int getAndIncrement()以原子方式将当前值加1
- int decrementAndGet()以原子方式将当前值减1
- int getAndIncrement()以原子方式将当前值减1
- int getAndAdd(int delta)以原子方式将给定值与当前值相加
- int addAndGet(int delta)以原子方式将给定值与当前值相加
- int get()获取当前值
CAS无锁机制
-CAS是英文单词Compara And Swap的缩写,翻译过来就是比较并替换.当多条线程尝试使用CAS同时更新
同一个变量时,只是其中一条线程能更新变量的值,而其他线程都失败,失败的线程并不会被挂起,而是告知
这次竞争失败,并可以再次尝试CAS的的三大缺点
- 构造方法
循环时间长开销很大
CAS通常是配合无限循环一起使用的,如果CAS失败,会一直进行尝试.如果CAS长时间一直不成功
可能会给CPU带来很大的开销
只能保证一个变量的原子操作
当对一个变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个变量操作时
CAS目前无法直接保证操作的原子性
ABA问题
- 第一条线程获取到V位置的值 假设是1
- 第二条线程获取到V位置的值 也是1
- 第一条线程成功 将值改为0
第一条线程又cas成功 将值改回1
这时第二条线程cas 发现值没变 还是1 cas成功<br /> 实际上当第二条线程cas时 v位置的值已经从1-0-1<br /> 这就是ABA问题<br /> 如何解决 每次获取v位置的值时,带上一个版本号,这样就可以避免ABA问题 java中AtomicStampedReference这<br /> 个类在cas时就是通过版本号来解决的悲观锁和乐观锁
-无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想
-悲观锁:简单来说操作系统会悲观的任务如果不严格同步线程调用那么一定会产生异常
所以互斥锁将会锁定资源,只供一个线程调用.而阻塞其他线程,因此这种同步机制也叫做
悲观锁
-乐观锁:比如CAS不会对资源进行锁定,而且当线程需要修改共享资源时,总是会乐观
认为值没有被其他线程修改过,而自己主动尝试修改对应的值.相较于悲观锁,这种同步机制
称为乐观锁
可见性
-当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程应该能够立即看到
修改的值
-如何保证可见性?
- 加锁,比如使用synchronized
JMM关于synchronized的两条规定
- 线程解锁前,必须把共享变量的最新值刷新到主内存中
线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取
最新的值
- 使用volatile关键字保证可见性
- 底层实现主要是通过汇编lock前缀指令,会锁住这块区域的缓存,并写回主内存.
1. 会将当前处理器缓存的行数据立即写回系统内存 1. 这个写回内存的操作导致CPU的缓存该内存地址的数值失效(MESI协议) 1. 注意:volatile只能保证可见性,但是不能保证原子性,如果要保证原子性,请使用锁
有序性
-一般来说,程序的执行顺序按照代码的先后顺序执行,但是处理器为了提高程序的效率,可能会对代码
的执行顺序进行优化,它不保证程序中各个语句的执行先后顺序一致,但是保证程序的最终结果和代码
顺序执行的结果一致
volatile关键字有两个作用 1.保证可见性 2.禁止重排序
注意:锁也可以保证有序性,因为在代码块中,一次只有一个线程执行,系统重排对单线程执行是没有任何影响的
单例设计模式
饿汉式:
优点:实现起来简单,没有多线程同步问题
缺点:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,
从这之后,这个static的instance对象便一直占着这段内存,当类被卸载时,静态变量被摧毁,并释放所占有的
内存,因此在某些特定条件下会耗费内存
懒汉式(延迟加载)
延迟加载就是调用get()方法时实例才被创建,常见的实现方法就是在get方法中进行new实例化
优点:实现起来比较简单,当类SingletonTest被加载的时候,静态变量static的instance没有创建对象
当getlnstance方法第一次被调用时,才创建对象为其赋值,因此在某些特定条件下会节约了内存
缺点:在多线程环境中,这种实现方法是完全错误的,根本不能保证单例的状态
Callable接口
-Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法叫做call():
-Callable是一个泛型接口,call()函数返回的类型就是传递进来的v类型
-如何使用Callable接口
- 定义类实现Callable接口
- 重写call方法
- 开启线程
Timer定时器
概述
java.util.Timer一种工具,线程用其安排以后在后台线程中执行的任务.可安排任务执行一次
或者定期重复执行.定时器是使用新建的线程来执行,这样即使主线程main结束了,定时器也依然会继续工作
构造方法:无参数
- 定时方法:
- public void schedule(TimerTask task,Date firstTime,longperiod)
- TimerTask是定时器要执行的任务,一个抽象类,我们需要继承并重写方法run()
- firstTime定时器开始执行的时间
- period时间间隔,毫秒值
