zoukankan      html  css  js  c++  java
  • Java并发编程的艺术(三)——synchronized

    什么是synchronized

    synchronized可以保证某个代码块或者方法被一个线程占有,保证了一个线程的可先性。java 1.6之前是重量级锁,在1.6进行了各种优化,就不那么重了,并引入了偏向锁和轻量级锁,以及锁的存储结构和升级过程。

    synchronized实现方式

    Java中每个对象都可以作为锁:

    • 对于普通同步方法,锁是实例对象。
    • 对于静态通同步方法,锁是当前类的Class对象。
    • 对于同步方法块,锁是Synchronized括号里的配置对象。

    synchronized实现原理

    synchronized可以保证方法或者代码块在运行时,同一个时刻只有一个线程可以进入到临界区,同时它还可以保证共享变量的可见性。

    JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。通过monitorenter和monitorexit指令实现。前者是编译后插入到同步代码块开始的位置,后者是插入到方法结束和异常处。JVM保证每个monitorenter和monitorexit指令是配对的,任何对象都会有一个monitor与之关联,当对对象的monitor被持有后,它就是处于锁定状态。线程执行到monitorenter时,就会尝试获取对象monitor的所有权,就是获取对象锁。

    sychronized 特点

    1. 不能被继承,但是可以调用父类的同步方法达到同步的目的。
    2. 一个线程访问一个对象的synchronized方法或者代码块的时候,其他线程访问该对象的synchronized方法或者代码块将被阻塞,但是非synchronized方法或者代码块还是可以访问 的。
    3. 不论sychronized 放在对象还是方法上,如果它作用的是非静态的,那么获得锁是对象,如果是静态的,那么获得锁是类,最后导致获得所有对象的锁。

    锁的升级和对比

    锁一共有四种状态:

    1. 无锁
    2. 偏向锁
    3. 轻量级锁
    4. 重量级锁

    锁的升级

    锁可以升级,但是不能降级。

    这样的规则目的是为了提高获得锁和释放锁的效率。

    偏向锁

    作用

    大多数情况下不存在锁的竞争,为了降低同一线程获得锁的开销,就在锁的对象头中加入这一线程的ID,这样,在之后这个线程进入和退出同步块的时候就不需要CAS操作来实现加锁和解锁。

    撤销

    当出现锁的竞争,持有偏向锁的线程才会释放锁,锁就会升级。

    轻量级锁

    作用

    利用CAS算法,线程通过自旋的方式获取锁。这样,线程不会被阻塞。

    加锁

    首先JVM在线程的栈帧中创建锁记录存储空间,然后把锁对象头重的Markword复制到栈帧中。在通过利用CAS方法将锁对象的markword替换为指向线程栈帧锁记录的指针。如果成功替换,就获得了当前锁,如果已经被其他线程替换了,那么就会自旋获取锁。

    解锁

    将栈帧的markword再利用CAS放回对象的头中,如果失败,就说明存在锁竞争,锁就会升级为重量级锁。

    锁的比较

    在这里插入图片描述

    synchronized使用

    修饰方法和代码块

    修饰方法很简单,就在方法前面加一个synchronized关键字。

    public synchronized void method()
    {
       // todo
    }
    
    public void method()
    {
       synchronized(this) {
          // todo
       }
    }
    

    指定给某个对象加锁

    public void method3(SomeObject obj)
    {
       //obj 锁定的对象
       synchronized(obj)
       {
          // todo
       }
    }
    

    上面的代码中,通过synchronized 给obj对象加锁。当其他线程想要访问obj,就会被阻塞。

    class Test implements Runnable
    {
       private byte[] lock = new byte[0];  // 特殊的instance变量
       public void method()
       {
          synchronized(lock) {
             // todo 同步代码块
          }
       }
     
       public void run() {
     
       }
    }
    

    如果只是想要一个锁,可以利用特殊的实例来充当锁。

    修饰静态的方法

    public synchronized static void method() {
       // todo
    }
    

    因为静态方法是属于这个类的, 而不是实例化的对象,所以,synchronized 修饰的是静态方法锁定的这个类的所有对象。

    修饰类

    class ClassName {
       public void method() {
          synchronized(ClassName.class) {
             // todo
          }
       }
    }
    

    和修饰静态方法一样,所有对象都共有这把类锁。

    参考: link.

  • 相关阅读:
    四则运算实现
    第四周例行报告
    代码规范,结对要求
    第三周例行报告
    第三周作业3功能测试
    第二周例行报告
    第一次作业汇总
    2017/2/24:Maven的pom jar war的区别
    oracle的常用99条语句
    2017/2/21:配置自己的中文乱码拦截器
  • 原文地址:https://www.cnblogs.com/lippon/p/14117675.html
Copyright © 2011-2022 走看看