前言

锁的种类很多,我们今天就来梳理一下。Java中的好多锁系列之悲观锁、乐观锁。

一:悲观锁

悲观的认为所有的线程都会导致数据错误,每一个线程都需要排队等待。优点:数据一致性,缺点:效率低

1.1:synchronized

  • jdk内置的锁,熟悉native的朋友会知道,synchronized是调用了jr.jar里的放

  • 灵魂的拷问,他是怎么实现的。答案对象头object head,通过Object Header对象头

Java对象的对象头由 Mark Word 和 Klass pointer 两部分组成,

mark word存储了同步状态、标识、hashcode、GC状态等等。

Klass pointer存储对象的类型指针,该指针指向它的类元数据

值得注意的是,如果应用的对象过多,使用64位的指针将浪费大量内存。64位的JVM比32位的JVM多耗费50%的内存。

我们现在使用的64位 JVM会默认使用选项 +UseCompressedOops 开启指针压缩,将指针压缩至32位。

以64位操作系统为例,对象头存储内容图例。

简单介绍一下各部分的含义 lock: 锁状态标记位,该标记的值不同,整个mark word表示的含义不同。 biased_lock:偏向锁标记,为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。 age:Java GC标记位对象年龄,4位最大是15,所以minor Gc的次数就是15次。 identity_hashcode:对象标识Hash码,采用延迟加载技术。当对象使用HashCode()计算后,并会将结果写到该对象头中。当对象被锁定时,该值会移动到线程Monitor中。 thread:持有偏向锁的线程ID和其他信息。这个线程ID并不是JVM分配的线程ID号,和Java Thread中的ID是两个概念。 epoch:偏向时间戳。 ptr_to_lock_record:指向栈中锁记录的指针。 ptr_to_heavyweight_monitor:指向线程Monitor的指针。 输出的第一行内容和锁状态内容对应 unused:1 | age:4 | biased_lock:1 | lock:2 0 0000 0 01 代表A对象正处于无锁状态

第三行中表示的是被指针压缩为32位的klass pointer 第四行则是我们创建的A对象属性信息 1字节的boolean值 第五行则代表了对象的对齐字段 为了凑齐64位的对象,对齐字段

  • 对象锁:synchronized

锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念。

synchronized是对类的当前实例(当前对象)进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”, 类的两个不同实例就没有这种约束了。

import java.util.concurrent.TimeUnit;

public class Test1 {

   public synchronized void start() {
       try {
           TimeUnit.SECONDS.sleep(2);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(Thread.currentThread().getName() + ",start");
  }

   public synchronized void end() {
       System.out.println(Thread.currentThread().getName() + ",end");
  }

   public static void main(String args[]) {
       Test1 test = new Test1();
       new Thread(test::start, "线程A").start();
       new Thread(test::end, "线程B").start();
  }
}

执行结果:

线程A,start 线程B,end

  • 类锁:static synchronized

该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。

public synchronized void start() {
   try {
       TimeUnit.SECONDS.sleep(2);
  } catch (InterruptedException e) {
       e.printStackTrace();
  }
   System.out.println(Thread.currentThread().getName() + ",start");
}

public synchronized void end() {
   System.out.println(Thread.currentThread().getName() + ",end");
}

public static void main(String args[]) {
   StaticTest a = new StaticTest();
   StaticTest b = new StaticTest();
   new Thread(a::start, "线程A").start();
   new Thread(b::end, "线程c").start();
}

执行结果:

线程A,end 线程c,start

更改start、end方法为static后,执行结果:

线程c,start 线程A,end

  • synchronized+static synchronized

public class StaticTest {

   public synchronized void start() {
       try {
           TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(Thread.currentThread().getName() + ",start");
  }

   public static synchronized void end() {
       System.out.println(Thread.currentThread().getName() + ",end");
  }


   public static void main(String args[]) {
       StaticTest a = new StaticTest();
       StaticTest b = new StaticTest();
       new Thread(() -> a.start(), "线程c").start();
       new Thread(() -> a.end(), "线程A").start();
  }
}

执行结果:

线程A,end 线程c,start

synchronized 与static synchronized 相当于两帮派,各自管各自,相互之间就无约束了,可以被同时访问。

1.2:Lock

未完待续

二:乐观锁

 

2.1:AtomicInteger

public class VersionTest {
   final static int LOOP = 10000;

   int count = 0;

   public void add() {
       count++;
       System.out.println(Thread.currentThread().getName() + " add number :" + count);
  }

   public void subtract() {
       count--;
       System.out.println(Thread.currentThread().getName() + " subtract number :" + count);
  }

   public static void main(String args[]) throws InterruptedException {
       VersionTest test = new VersionTest();
       for (int i = 0; i < VersionTest.LOOP; i++) {
           new Thread(() -> test.add()).start();
           new Thread(() -> test.subtract()).start();
      }
       TimeUnit.SECONDS.sleep(1);
       System.out.println(test.count);
  }
}
执行结果:
Thread-19996 add number :1
Thread-19997 subtract number :0
Thread-19998 add number :1
Thread-19999 subtract number :0
Thread-19962 add number :1
Thread-19971 subtract number :0
Thread-19975 subtract number :-1
Thread-19881 subtract number :-2
-2
   
public class AtomicIntegerTest {
   final static int LOOP = 10000;

    AtomicInteger count = new AtomicInteger(0);

   public void add() {
       count.addAndGet(1);
       System.out.println(Thread.currentThread().getName() + " add number :" + count.get());
  }

   public void subtract() {
       count.decrementAndGet();
       System.out.println(Thread.currentThread().getName() + " subtract number :" + count.get());
  }

   public static void main(String args[]) throws InterruptedException {
       AtomicIntegerTest test = new AtomicIntegerTest();
       for (int i = 0; i < AtomicIntegerTest.LOOP; i++) {
           new Thread(() -> test.add()).start();
           new Thread(() -> test.subtract()).start();
      }
       TimeUnit.SECONDS.sleep(1);
       System.out.println(test.count.get());
  }
}

执行结果:

Thread-19989 subtract number :0 Thread-19988 add number :1 Thread-19973 subtract number :0 Thread-19990 add number :1 Thread-19999 subtract number :0 Thread-19996 add number :1 Thread-19995 subtract number :0 0

2.2:CAS算法

compare and swap比较与交换,实现原理:

当前主内存变量的值V线程本地变量预期值A(主内存变量V的副本),线程本地待更新值B。当需要更新变量值的时候,会先获取到内存变量值V然后跟预期值A进行比较,如果相同则更新为B,如果不同,线程本地变量预期值A更新为主内存变量V的值

版权声明:本文为rookiejava原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/rookiejava/p/12081126.html