Java多线程学习笔记 (5)

明确知道多线程的情况下,不一定。
因为偏向锁在多线程情况下,会涉及到锁撤销,这个时候直接使用自旋锁,JVM启动过程,会有很多线程竞争,比如启动的时候,肯定是多线程的,所以默认情况,启动时候不打开偏向锁,过一段时间再打开。
有一个参数可以配置:BiasedLockingStartupDelay默认是4s钟

偏向锁状态下,调用了wait方法,直接升级成重量级锁

一个线程拿20个对象进行加锁,批量锁的重偏向(20个对象),批量锁撤销(变成轻量级锁)(40个对象), 通过Epoch中的值和对应的类对象里面记录的值比较。

synchronized 锁定对象 public class SynchronizedObject implements Runnable { static SynchronizedObject instance = new SynchronizedObject(); final Object object = new Object(); static volatile int i = 0; @Override public void run() { for (int j = 0; j < 1000000; j++) { // 任何线程要执行下面的代码,必须先拿到object的锁 synchronized (object) { i++; } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } } 锁定方法

锁定静态方法相当于锁定当前类

public class SynchronizedStatic implements Runnable { static SynchronizedStatic instance = new SynchronizedStatic(); static volatile int i = 0; @Override public void run() { increase(); } // 相当于synchronized(SynchronizedStatic.class) synchronized static void increase() { for (int j = 0; j < 1000000; j++) { i++; } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }

锁定非静态方法相当于锁定该对象的实例或synchronized(this)

public class SynchronizedMethod implements Runnable { static SynchronizedMethod instance = new SynchronizedMethod(); static volatile int i = 0; @Override public void run() { increase(); } void increase() { for (int j = 0; j < 1000000; j++) { // 任何线程要执行下面的代码,必须先拿到object的锁 synchronized (this) { i++; } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } } 脏读 public class DirtyRead { String name; double balance; public static void main(String[] args) { DirtyRead a = new DirtyRead(); Thread thread = new Thread(() -> a.set("zhangsan", 100.0)); thread.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(a.getBalance("zhangsan")); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(a.getBalance("zhangsan")); } public synchronized void set(String name, double balance) { this.name = name; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.balance = balance; } // 如果get方法不加synchronized关键字,就会出现脏读情况 public /*synchronized*/ double getBalance(String name) { return this.balance; } }

其中的getBalance方法,如果不加synchronized,就会产生脏读的问题。

可重入锁

一个同步方法可以调用另外一个同步方法,
一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁(可重入锁)
子类synchronized,如果调用父类的synchronize方法:super.method(),如果不可重入,直接就会死锁。

public class SynchronizedReentry implements Runnable { public static void main(String[] args) throws IOException { SynchronizedReentry myRun = new SynchronizedReentry(); Thread thread = new Thread(myRun, "t1"); Thread thread2 = new Thread(myRun, "t2"); thread.start(); thread2.start(); System.in.read(); } synchronized void m1(String content) { System.out.println(this); System.out.println("m1 get content is " + content); m2(content); } synchronized void m2(String content) { System.out.println(this); System.out.println("m2 get content is " + content); } @Override public void run() { m1(Thread.currentThread().getName()); } }

程序在执行过程中,如果出现异常,默认情况锁会被释放 ,所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适, 在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。因此要非常小心的处理同步业务逻辑中的异常。

示例见:

SynchronizedException.java

synchronized的底层实现

在早期的JDK使用的是OS的重量级锁

后来的改进锁升级的概念:

synchronized (Object)

markword 记录这个线程ID (使用偏向锁)

如果线程争用:升级为 自旋锁

10次自旋以后,升级为重量级锁 - OS

所以:

执行时间短(加锁代码),线程数少,用自旋

执行时间长,线程数多,用系统锁

synchronized不能锁定String常量,Integer,Long等基础类型

见示例:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppdzy.html