多线程之synchronized

112 阅读4分钟

synchronized

synchronized基础特性

  1. Synchronized是Java中的一个关键字
  2. 非公平锁,互斥锁
  3. 修饰的对象主要是方法和代码块
  • 方法
非静态方法: 锁住的是当前实例对象
静态方法:当前类的Class实例
  • 代码块
this: 锁住的是当前实例对象
实例对象: 锁住的是当前实例对象
class: 当前类的Class实例

synchronized具体示例

1. 无synchronized关键字

private static int count = 0;
public static void main(String[] args) {
    noSync();
}
public static void noSync(){
    SynchronizedTest test = new SynchronizedTest();
    for (int i = 0; i < 5; i ++) {
        Thread thread = new Thread(() -> {
            test.testNoSync();
        });
        thread.start();
    }
}

public void testNoSync(){
    System.out.println(String.format("testNoSync:%s 开始 count:%s", Thread.currentThread().getName(), count));
    try {
        TimeUnit.MILLISECONDS.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    count++;
    System.out.println(String.format("testNoSync:%s 结束 count:%s", Thread.currentThread().getName(), count));
}

image.png
上图运行结果分析

  • count没有按0~5的顺序打印出来;线程无法保证有序
  • count最终不为5,线程不安全

2. 方法上加synchronized关键字,线程间共同一个实例对象

private static int count = 0;
public static void main(String[] args) {
    methodSingleSync();
}
public static void methodSingleSync(){
    SynchronizedTest test = new SynchronizedTest();
    for (int i = 0; i < 5; i ++) {
        Thread thread = new Thread(() -> {
            test.testMethodSingleSync();
        });
        thread.start();
    }
}

public synchronized void testMethodSingleSync(){
    System.out.println(String.format("testMethodSingleSync:%s 开始 count:%s", Thread.currentThread().getName(), count));
    try {
        TimeUnit.MILLISECONDS.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    count++;
    System.out.println(String.format("testMethodSingleSync:%s 结束 count:%s", Thread.currentThread().getName(), count));
}

image.png
上图运行结果分析

  • count按0~5的顺序打印出来,线程间串行执行
  • count最终为5,线程安全

3. 方法上加synchronized关键字,线程间各自有一个实例对象

private static int count = 0;
public static void main(String[] args) {
    methodMultiSync();
}
public static void methodMultiSync(){
    for (int i = 0; i < 5; i ++) {
        SynchronizedTest test = new SynchronizedTest();
        Thread thread = new Thread(() -> {
            test.testMethodMultiSync();
        });
        thread.start();
    }
}

public synchronized void testMethodMultiSync(){
    System.out.println(String.format("testMethodMultiSync:%s 开始 count:%s", Thread.currentThread().getName(), count));
    try {
        TimeUnit.MILLISECONDS.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    count++;
    System.out.println(String.format("testMethodMultiSync:%s 结束 count:%s", Thread.currentThread().getName(), count));
}

image.png
上图运行结果分析

  • 线程间并行执行和没加synchronized关键字时表现一样
  • count没按0~5顺序打印
  • count最终值不为5

由示例2、3分析可知道,synchronized修饰非静态方法时,锁住的是当前具体实例对象


4. 静态方法上加synchronized关键字,且线程间各自有一个实例对象

private static int count = 0;
public static void main(String[] args) {
    methodStaticSync();
}
public static void methodStaticSync(){
    for (int i = 0; i < 5; i ++) {
        SynchronizedTest test = new SynchronizedTest();
        Thread thread = new Thread(() -> {
            test.testMethodStaticSync();
        });
        thread.start();
    }
}

public synchronized static void testMethodStaticSync(){
    System.out.println(String.format("testMethodStaticSync:%s 开始 count:%s", Thread.currentThread().getName(), count));
    try {
        TimeUnit.MILLISECONDS.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    count++;
    System.out.println(String.format("testMethodStaticSync:%s 结束 count:%s", Thread.currentThread().getName(), count));
}

image.png
上图运行结果分析

  • count按0~5的顺序打印出来,线程间串行执行
  • count最终为5,线程安全

由示例4中可知,就算线程间各自拥有一个实例,但线程间依然安全。因为synchronized修饰静态方法时锁住的是当前classc对象,而非具体实例对象。


5. 加在代码块中,修饰this,线程间共同一个实例对象

private static int count = 0;
public static void main(String[] args) {
    codeThisSingleSync();
}
public static void codeThisSingleSync(){
    SynchronizedTest test = new SynchronizedTest();
    for (int i = 0; i < 5; i ++) {
        Thread thread = new Thread(() -> {
            test.testCodeThisSingleSync();
        });
        thread.start();
    }
}

public void testCodeThisSingleSync(){
    System.out.println(String.format("testCodeThisSingleSync:%s outer 开始 count:%s", Thread.currentThread().getName(), count));
    synchronized (this){
        System.out.println(String.format("testCodeThisSingleSync:%s inner 开始 count:%s", Thread.currentThread().getName(), count));
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
        System.out.println(String.format("testCodeThisSingleSync:%s inner 结束 count:%s", Thread.currentThread().getName(), count));
    }

    System.out.println(String.format("testCodeThisSingleSync:%s outer 结束 count:%s", Thread.currentThread().getName(), count));
}

image.png
上图运行结果分析

  • count按0~5的顺序打印出来,代码块内线程间互斥,串行执行
  • count最终为5,原因是count++在代码块内保证了其最终结果的正确性
  • 上图圈出绿色部分可知,synchronized代码块内串行执行,但代码块之外的还是会被其他线程穿插进来

6. 加在代码块中,修饰this,线程间各自一个实例对象

private static int count = 0;
public static void main(String[] args) {
    codeThisMultiSync();
}
public static void codeThisMultiSync(){
    for (int i = 0; i < 5; i ++) {
        SynchronizedTest test = new SynchronizedTest();
        Thread thread = new Thread(() -> {
            test.testCodeThisMultiSync();
        });
        thread.start();
    }
}

public void testCodeThisMultiSync(){
    System.out.println(String.format("testCodeThisMultiSync:%s outer 开始 count:%s", Thread.currentThread().getName(), count));

    synchronized (this){
        System.out.println(String.format("testCodeThisMultiSync:%s inner 开始 count:%s", Thread.currentThread().getName(), count));
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
        System.out.println(String.format("testCodeThisMultiSync:%s inner 结束 count:%s", Thread.currentThread().getName(), count));
    }

    System.out.println(String.format("testCodeThisMultiSync:%s outer 结束 count:%s", Thread.currentThread().getName(), count));
}

image.png
上图运行结果分析

  • count没按0~5的顺序打印出来,count最终值也不为5
  • 上图圈出绿色部分可知,synchronized代码块内的也没保证完整执行,被其他线程插入

由示例5、6可知synchronized修饰代码块中的this时,锁住的是当前实例对象


7. 加在代码块中,修饰Object

private static int count = 0;
public static void main(String[] args) {
    codeObjSync1();
    //codeObjSync2();
}

//1、修饰同个Object
public static void codeObjSync1(){
    Object obj = new Object();
    for (int i = 0; i < 5; i ++) {
        SynchronizedTest test = new SynchronizedTest();
        Thread thread = new Thread(() -> {
            test.testCodeObjSync(obj);
        });
        thread.start();
    }
}

//2、修饰多个object
public static void codeObjSync2(){
    for (int i = 0; i < 5; i ++) {
        Object obj = new Object();
        SynchronizedTest test = new SynchronizedTest();
        Thread thread = new Thread(() -> {
            test.testCodeObjSync(obj);
        });
        thread.start();
    }
}



public void testCodeObjSync(Object obj){
    System.out.println(String.format("testCodeObjSync:%s outer 开始 count:%s", Thread.currentThread().getName(), count));
    synchronized (obj){
        System.out.println(String.format("testCodeObjSync:%s inner 开始 count:%s", Thread.currentThread().getName(), count));
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
        System.out.println(String.format("testCodeObjSync:%s inner 结束 count:%s", Thread.currentThread().getName(), count));
    }

    System.out.println(String.format("testCodeObjSync:%s outer 结束 count:%s", Thread.currentThread().getName(), count));
}

修饰同个Object运行结果如下图

image.png

修饰各自Object运行结果如下图

image.png

由示例7可知synchronized修饰代码块中的Object对象时,锁住的是当前实例对象,和this一样


8. 加在代码块中,修饰class

private static int count = 0;
public static void main(String[] args) {
    codeClassSync();
}

public static void codeClassSync(){
    for (int i = 0; i < 5; i ++) {
        SynchronizedTest test = new SynchronizedTest();
        Thread thread = new Thread(() -> {
            test.testCodeClassSync();
        });
        thread.start();
    }
}

public void testCodeClassSync(){
    System.out.println(String.format("testCodeClassSync:%s outer 开始 count:%s", Thread.currentThread().getName(), count));
    synchronized (SynchronizedTest.class){
        System.out.println(String.format("testCodeClassSync:%s inner 开始 count:%s", Thread.currentThread().getName(), count));
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
        System.out.println(String.format("testCodeClassSync:%s inner 结束 count:%s", Thread.currentThread().getName(), count));
    }

    System.out.println(String.format("testCodeClassSync:%s outer 结束 count:%s", Thread.currentThread().getName(), count));
}

image.png

上图运行结果分析

  • count按0~5的顺序打印出来,线程间串行执行
  • count最终为5,线程安全

由示例8中可知,就算线程间各自拥有一个实例,但线程间依然安全。因为synchronized修饰代码块中的class时锁住的是当前classc对象,而非具体实例对象。