多线程与高并发——synchronized

Synchronized

线程锁:Synchronized

Synchronized的自我理解

Synchronized锁的是什么?

​ synchronized这个锁,它其实就是在后面括号中所跟对象的头部加入一定得代码,后续所有想要用这个锁对象的,都会去看一下有没有这个代码,如果有,说明就被锁住了,具体的逻辑分析在下图:
多线程与高并发——synchronized

Synchronized锁对象的选用

常见的Synchronized用法

方式1:

public static void getInstance() {
synchronized (DlcDoubleCheckLock.class) {
   ....
  }
   }

方式2:

public static synchronized void getInstance() {
if (dlcDoubleCheckLock == null) {
   .....
  }
   }

假设使用了方式1的形式进行上锁,那么又可以写很多的方式:

// 形式1
public static void getInstance() {
synchronized (DlcDoubleCheckLock.class) {
   ....
  }
   }
  // 形式2
public static void getInstance() {
synchronized (this) {
   ....
  }
   }
  // 形式3
Object o = new Object();
public static void getInstance() {
synchronized (o) {
   ....
  }
   }
形式1的锁测试
public class SyncTestClass {
   void m1() throws InterruptedException {
       synchronized (SyncTestClass.class){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(SyncTestClass.class));
           for(int i = 0;i<5;i++){
               System.out.println("m1"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }
   void m2() throws InterruptedException {
       synchronized (SyncTestClass.class){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(SyncTestClass.class));
           for(int i = 0;i<5;i++){
               System.out.println("m2"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }

   public static void main(String[] args) throws InterruptedException {
       SyncTestClass syncTest1 = new SyncTestClass();
       SyncTestClass syncTest2 = new SyncTestClass();
       // t1、t2为不同实例化对象调用方法
       // t3、t4为相同实例化对象调用方法
       Thread t1 = new Thread(()-> {
           try {
               syncTest1.m1();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       Thread t2 = new Thread(()-> {
           try {
               syncTest2.m2();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println("======================");
       Thread t3 = new Thread(()-> {
           try {
               syncTest1.m1();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       Thread t4 = new Thread(()-> {
           try {
               syncTest1.m2();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       t3.start();
       t4.start();
       t3.join();
       t4.join();
   }
}

输出结果如下:

Thread-1得到锁
当前锁对象:685325104
m20
m21
m22
m23
m24
Thread-1释放锁
Thread-0得到锁
当前锁对象:685325104
m10
m11
m12
m13
m14
Thread-0释放锁
======================
Thread-2得到锁
当前锁对象:685325104
m10
m11
m12
m13
m14
Thread-2释放锁
Thread-3得到锁
当前锁对象:685325104
m20
m21
m22
m23
m24
Thread-3释放锁

结果分析:

​ 如果是用类作为锁的对象,那么这把锁就是加在这唯一的一个类上面,无论实例化多少对象,这个类都是唯一的,所以锁对象是唯一的。

形式2的锁测试
public class SyncTestThis {
   void m1() throws InterruptedException {
       synchronized (this){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(this));
           for(int i = 0;i<5;i++){
               System.out.println("m1"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }
   void m2() throws InterruptedException {
       synchronized (this){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(this));
           for(int i = 0;i<5;i++){
               System.out.println("m2"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }
   void m3() throws InterruptedException {
       synchronized (this){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(this));
           for(int i = 0;i<5;i++){
               System.out.println("m1"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }
   void m4() throws InterruptedException {
       synchronized (this){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(this));
           for(int i = 0;i<5;i++){
               System.out.println("m2"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }

   public static void main(String[] args) throws InterruptedException {
       SyncTestThis syncTest1 = new SyncTestThis();
       SyncTestThis syncTest2 = new SyncTestThis();
       // t1、t2用来测试不同的实例化对象造成的影响
       // t3、t4用来测试相同的实例化对象造成的影响
       Thread t1 = new Thread(()-> {
           try {
               syncTest1.m1();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       Thread t2 = new Thread(()-> {
           try {
               syncTest2.m2();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println("==============================");
       Thread t3 = new Thread(()-> {
           try {
               syncTest1.m3();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       Thread t4 = new Thread(()-> {
           try {
               syncTest1.m4();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       t3.start();
       t4.start();
       t3.join();
       t4.join();
   }
}

输出结果如下:

Thread-0得到锁
Thread-1得到锁
当前锁对象:422057313
当前锁对象:2115147268
m20
m10
m11
m21
m22
m12
m13
m23
m14
m24
Thread-1释放锁
Thread-0释放锁
==============================
Thread-2得到锁
当前锁对象:422057313
m10
m11
m12
m13
m14
Thread-2释放锁
Thread-3得到锁
当前锁对象:422057313
m20
m21
m22
m23
m24
Thread-3释放锁

结果分析:

​ this指代的就是当前实例化的对象,如果说当前实例化的对象不一致,那么这个锁就不会在两个线程之间相互影响,但是如果说是同一个实例化对象,那么,这个锁就会起到作用,且这把锁,在同一个实例化对象内部的不同方法间也会起到作用,具体可以从锁对象的值上面看到。

形式3的锁测试
public class SyncTestExtenalObj {
   private final Object o = new Object();
   void m1() throws InterruptedException {
       synchronized (o){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(o));
           for(int i = 0;i<5;i++){
               System.out.println("m1"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }
   void m2() throws InterruptedException {
       synchronized (o){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(o));
           for(int i = 0;i<5;i++){
               System.out.println("m2"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }

   void m3() throws InterruptedException {
       Object innerObj = new Object();
       synchronized (innerObj){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(innerObj));
           for(int i = 0;i<5;i++){
               System.out.println("m3"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }
   void m4() throws InterruptedException {
       Object innerObj = new Object();
       synchronized (innerObj){
           System.out.println(Thread.currentThread().getName() + "得到锁");
           System.out.println("当前锁对象:" + System.identityHashCode(innerObj));
           for(int i = 0;i<5;i++){
               System.out.println("m4"+i);
               TimeUnit.SECONDS.sleep(1);
           }
       }
       System.out.println(Thread.currentThread().getName() + "释放锁");
   }

   public static void main(String[] args) throws InterruptedException {
       SyncTestExtenalObj syncTest1 = new SyncTestExtenalObj();
       // t1、t2为不同实例化对象调用方法
       // t3、t4为相同实例化对象调用方法
       Thread t1 = new Thread(()-> {
           try {
               syncTest1.m1();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       Thread t2 = new Thread(()-> {
           try {
               syncTest1.m2();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println("======================");
       Thread t3 = new Thread(()-> {
           try {
               syncTest1.m3();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       Thread t4 = new Thread(()-> {
           try {
               syncTest1.m4();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       });
       t3.start();
       t4.start();
       t3.join();
       t4.join();
   }
}

输出结果如下:

Thread-0得到锁
当前锁对象:503674368
m10
m11
m12
m13
m14
Thread-1得到锁
当前锁对象:503674368
m20
Thread-0释放锁
m21
m22
m23
m24
Thread-1释放锁
======================
Thread-3得到锁
当前锁对象:650310003
m40
Thread-2得到锁
当前锁对象:1670595893
m30
m41
m31
m42
m32
m43
m33
m44
m34
Thread-3释放锁
Thread-2释放锁

结果分析:

​ 如果用公用的一个内部Object,那么它实际上锁住的是那个实例化后的对象o,如果此时是不同的实例,那么这个内部对象o也就无意义了。

用整型Integer的形式做锁是否可行?

测试代码1
/**
* 不同实例对象下,调用同一个方法,观察是否能加锁成功
* 此时采用Integer作为锁,且值为1
*/
public class SyncTestInteger1 {
    private final Integer lock = 1;
    void m1() throws InterruptedException {
        synchronized (lock){
            System.out.println(Thread.currentThread().getName() + "得到锁");
            System.out.println("当前锁对象:" + System.identityHashCode(lock));
            for(int i = 0;i<5;i++){
                System.out.println("m1"+i);
                TimeUnit.SECONDS.sleep(1);
            }
            System.out.println(Thread.currentThread().getName() + "释放锁");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTestInteger1 syncTest1 = new SyncTestInteger1();
        SyncTestInteger1 syncTest2 = new SyncTestInteger1();
        Thread t1 = new Thread(()-> {
            try {
                syncTest1.m1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(()-> {
            try {
                syncTest2.m1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

返回结果如下:

Thread-1得到锁
当前锁对象:118531371
m10
m11
m12
m13
m14
Thread-1释放锁
Thread-0得到锁
当前锁对象:118531371
m10
m11
m12
m13
m14
Thread-0释放锁
测试代码2
/**
* 不同实例对象下,调用同一个方法,观察是否能加锁成功
* 此时采用Integer作为锁,且值为1024
*/
public class SyncTestInteger {
    private final Integer lock = 1024;
    void m1() throws InterruptedException {
        synchronized (lock){
            System.out.println(Thread.currentThread().getName() + "得到锁");
            System.out.println("当前锁对象:" + System.identityHashCode(lock));
            for(int i = 0;i<5;i++){
                System.out.println("m1"+i);
                TimeUnit.SECONDS.sleep(1);
            }
            System.out.println(Thread.currentThread().getName() + "释放锁");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTestInteger syncTest1 = new SyncTestInteger();
        Thread t1 = new Thread(()-> {
            try {
                syncTest1.m1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(()-> {
            try {
                syncTest1.m1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

返回结果如下:

Thread-0得到锁
Thread-1得到锁
当前锁对象:118531371
当前锁对象:733365800
m10
m10
m11
m11
m12
m12
m13
m13
m14
m14
Thread-0释放锁
Thread-1释放锁
测试代码3
public class SyncTestInteger2 {
    private final Integer lock1 = 1024;
    private final Integer lock2 = 1024;
    void m1() throws InterruptedException {
        synchronized (lock1){
            System.out.println(Thread.currentThread().getName() + "得到锁");
            System.out.println("当前锁对象:" + System.identityHashCode(lock1));
            for(int i = 0;i<5;i++){
                System.out.println("m1"+i);
                TimeUnit.SECONDS.sleep(1);
            }
            System.out.println(Thread.currentThread().getName() + "释放锁");
        }
    }
    void m2() throws InterruptedException {
        synchronized (lock2){
            System.out.println(Thread.currentThread().getName() + "得到锁");
            System.out.println("当前锁对象:" + System.identityHashCode(lock2));
            for(int i = 0;i<5;i++){
                System.out.println("m1"+i);
                TimeUnit.SECONDS.sleep(1);
            }
            System.out.println(Thread.currentThread().getName() + "释放锁");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTestInteger2 syncTest1 = new SyncTestInteger2();
        Thread t1 = new Thread(()-> {
            try {
                syncTest1.m1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(()-> {
            try {
                syncTest1.m2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

返回结果如下:

Thread-0得到锁
Thread-1得到锁
当前锁对象:72786614
当前锁对象:1430746659
m10
m10
m11
m11
m12
m12
m13
m13
m14
m14
Thread-0释放锁
Thread-1释放锁
结果分析

​ 测试1和测试2中的两个锁对象,仅仅只是改变了lock的值,就运行出了截然不同的效果,测试1中可以发现两个方法是并行的,而测试2中却是逐步执行了,这是因为在Integer这个派生类内部维护着一个缓存池,缓存-128~127之间的值,这些值所指向的对象都是同一个,所以就导致了两种结果截然不同了,测试3中便印证了两个不同的Integer对象,值虽然相同,但是从锁对象上来看,完全是不同的,下面的截图也可以印证这一点
多线程与高并发——synchronized

在Integer内部也有具体说明:

/**
 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.
 *
 * The cache is initialized on first usage.  The size of the cache
 * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 * During VM initialization, java.lang.Integer.IntegerCache.high property
 * may be set and saved in the private system properties in the
 * sun.misc.VM class.
 */

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

所以如果真的想用Integer来作为锁,那么范围一定不要再-128~127之间选择,当然,也可以根据实际情况进行选择,建议是不要使用基础类型来作为锁对象。

用String对象是否可行?

String对象下,内部存在一个缓存池的机制,如下:
多线程与高并发——synchronized

即一个值被创建为String对象后,此次的jvm虚拟机中,就已经把当前值的地址记录下来了,后续不论怎么创建,都将继续采用这个地址,只要值一样,不论怎么创建。

所以通过这个原理,就可以知道,假设以String作为锁对象,那么,只要String内容一致,锁就是通用的,在各方法内都是这个地址的锁。

上一篇:每日总结


下一篇:chapter3\a\pmtest1.asm