Java关键字 volatile、synchronized和Lock

一、volatile:

简介:用来声明变量的值可能随时会受到其它线程的修改,使用volatile修饰的变量被修改后会立即强制将修改后的值写入主存,主存中的值的更新会使缓存中的值失效 。也就是当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的三大特性
1. 可见性
当多个线程访问同一个变量时,某一个线程修改了变量的值,其他线程能够立即读取到该变量修改后的值。
2. 有序性(禁止指令重排序)
即程序执行时按照代码书写的先后顺序执行。 在Java内存模型中,允许编译器和处理器对指令进行重排序, 重排序过程虽然不会影响到单线程程序的执行,但是会影响到多线程并发执行的正确性。
3. 不具备原子性
原子是世界上的最小单位,具有不可分割性。比如 i=0 这个操作是不可分割的,那么我们说这个操作是原子操作。再比如:i++这个操作实际是i = i +1是可分割的,所以它不是一个原子操作。非原子操作都会存在线程安全问题
注意: volatile不会让线程阻塞,所以响应速度比synchronized快,这是它的优点

二、synchronized 同步锁

功能:

  1. 修饰代码块:
    被修饰的代码块称为同步语句块,
    其作用的范围是大括号{}括起来的代码,
    作用的对象是调用这个代码块的对象
    一个线程访问一个对象中的synchronized(this)同步代码块时,
    其他试图访问该对象的线程将被阻塞。
    说明:
    当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,
    在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。
    Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,
    只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
    每通过new关键字创建一个对象都是不同的对象,不能称作同一对象。

  2. 修饰方法:
    被修饰的方法称为同步方法,
    其作用的范围是整个方法,
    作用的对象是调用这个方法的对象
    当一个线程访问对象的一个synchronized(this)同步代码块时,
    另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。
    说明:
    countAdd是一个synchronized的,printCount是非synchronized的。
    一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。

  3. 修饰静态的方法:
    其作用的范围是整个静态方法,
    作用的对象是这个类的所有对象
    synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,
    修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。
    静态方法是属于类的而不属于对象的。
    synchronized修饰的静态方法锁定的是这个类的所有对象。
    说明:
    两种写法:
    1. public synchronized void method () {}
    2. public void method () { synchronized (this) {} }
    注意:
    1、 synchronized关键字不能继承。
    2、 在定义接口方法时不能使用synchronized关键字。
    3、 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步

  4. 修饰类:
    其作用的范围是synchronized后面括号括起来的部分,
    作用主的对象是这个类的所有对象
    synchronized作用于一个类T时,
    是给这个类T加锁,T的所有对象用的是同一把锁。

三、Lock

  1. 通过Lock可以知道线程有没有成功获取到锁。synchronized无法办到的。
  2. Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。 Lock是一个类,通过这个类可以实现同步访问
  3. synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定, 而且在代码执行时出现异常,JVM会自动释放锁定, 但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
  4. 在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock, 但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,

lock的四个取锁方法:
lock() 就是用来获取锁。如果锁已被其他线程获取,则进行等待。

tryLock() 有返回值,表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false, 也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

tryLock(long time, TimeUnit unit) 这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。 如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

lockInterruptibly() 在获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。 也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时, 假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

注意:
使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,
以保证锁一定被释放,防止死锁的发生。转载:

发布了30 篇原创文章 · 获赞 12 · 访问量 3459

猜你喜欢

转载自blog.csdn.net/zx1293406/article/details/103548835