1.并发编程三要素
- 原子性原子,即一个不可再被分割的颗粒。在Java中原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
- 有序性程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
- 可见性当多个线程访问同一个变量时,如果其中一个线程对其作了修改,其他线程能立即获取到最新的值。
2. 线程的五大状态
- 创建状态当用 new 操作符创建一个线程的时候
- 就绪状态调用 start 方法,处于就绪状态的线程并不一定马上就会执行 run 方法,还需要等待CPU的调度
- 运行状态CPU 开始调度线程,并开始执行 run 方法
- 阻塞状态线程的执行过程中由于一些原因进入阻塞状态比如:调用 sleep 方法、尝试去得到一个锁等等
- 死亡状态run 方法执行完 或者 执行过程中遇到了一个异常
3.悲观锁与乐观锁
- 悲观锁:每次操作都会加锁,会造成线程阻塞。
- 乐观锁:每次操作不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止,不会造成线程阻塞。
4.valitate 关键字
4.1 定义
java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。
valitate是轻量级的synchronized,不会引起线程上下文的切换和调度,执行开销更小。
4.2 原理
1. 使用volitate修饰的变量在汇编阶段,会多出一条lock前缀指令
2. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成
3. 它会强制将对缓存的修改操作立即写入主存
4. 如果是写操作,它会导致其他CPU里缓存了该内存地址的数据无效
4.3 作用
内存可见性多线程操作的时候,一个线程修改了一个变量的值 ,其他线程能立即看到修改后的值防止重排序即程序的执行顺序按照代码的顺序执行(处理器为了提高代码的执行效率可能会对代码进行重排序)
并不能保证操作的原子性
5. synchronized 关键字
确保线程互斥的访问同步代码
5.1 定义
synchronized 是JVM实现的一种锁,其中锁的获取和释放分别是monitorenter 和 monitorexit 指令,该锁在实现上分为了偏向锁、轻量级锁和重量级锁,其中偏向锁在 java1.6 是默认开启的,轻量级锁在多线程竞争的情况下会膨胀成重量级锁,有关锁的数据都保存在对象头中
5.2 原理
加了 synchronized 关键字的代码段,生成的字节码文件会多出 monitorenter 和 monitorexit 两条指令(利用javap -verbose 字节码文件可看到关,关于这两条指令的文档如下:
-
monitorenter Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.
-
monitorexitThe thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.
加了 synchronized 关键字的方法,生成的字节码文件中会多一个 ACC_SYNCHRONIZED 标志位,当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
5.3 关于使用
- 修饰普通方法同步对象是实例对象
- 修饰静态方法同步对象是类本身
- 修饰代码块可以自己设置同步对象
5.4 缺点
会让没有得到锁的资源进入Block状态,争夺到资源之后又转为Running状态,这个过程涉及到操作系统用户模式和内核模式的切换,代价比较高。Java1.6为 synchronized 做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重量级锁之后,性能仍然较低。