synchronized三种使用方式(有图)

下一篇: 并发编程的基础

什么是线程安全

在这里插入图片描述
从图中可以看出线程安全只要包括变量在多个线程之间共享、生命周期内状态可以改变、最后结果和预期一致,就是线程安全的。

Synchronized的三种使用方式

1.普通同步方法(实例方法)锁是当前实例对象,进入同步代码前要获得当前实例的锁
2.静态同步方法,锁是当前类的class对象,进入同步代码前要获得当前类对象的锁
3.同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

作用在实例方法

  • 多个线程访问同一个对象的同一个synchronized方法
public class SynchronizedTest implements Runnable{

    static int i = 0;

    /**
     * 修饰实例方法
     */
    public synchronized void increase(){
        System.out.println("测试:"+Thread.currentThread().getName()+",i="+i++);
    }

    @Override
    public void run() {
        for(int j = 0;j < 10; j++){
            increase();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        Thread t1 = new Thread(synchronizedTest);
        Thread t2 = new Thread(synchronizedTest);
        t1.setName("线程1");
        t1.start();
        t1.join();
        t2.setName("线程2");
        t2.start();
        t2.join();
        System.out.println(i);
    }
}

执行结果:

测试:线程1,i=0
测试:线程1,i=1
测试:线程1,i=2
测试:线程1,i=3
测试:线程1,i=4
测试:线程1,i=5
测试:线程1,i=6
测试:线程1,i=7
测试:线程1,i=8
测试:线程1,i=9
测试:线程2,i=10
测试:线程2,i=11
测试:线程2,i=12
测试:线程2,i=13
测试:线程2,i=14
测试:线程2,i=15
测试:线程2,i=16
测试:线程2,i=17
测试:线程2,i=18
测试:线程2,i=19
20

从结果看线程1和线程2顺序执行,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其它线程无法获得该对象的锁,只有锁释放以后才可以获得,所以保证了正确的执行结果。

  • 多个线程访问同一个对象的不同的synchronized方法

public class SynchronizedTest{

   public synchronized void testMethod1(){
       System.out.println("testMethod1 start:"+Thread.currentThread().getName());
       try {
           System.out.println("testMethod1:"+Thread.currentThread().getName());
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("testMethod1 end:"+Thread.currentThread().getName());
   }

   public synchronized void testMethod2(){
       System.out.println("testMethod2 start:"+Thread.currentThread().getName());
       try {
           System.out.println("testMethod2:"+Thread.currentThread().getName());
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("testMethod2 end:"+Thread.currentThread().getName());
   }

   public static void main(String[] args) throws InterruptedException {
       SynchronizedTest synchronizedTest = new SynchronizedTest();
       new Thread(new Runnable() {
           @Override
           public void run() {
               synchronizedTest.testMethod1();
           }
       },"线程1").start();
       new Thread(new Runnable() {
           @Override
           public void run() {
               synchronizedTest.testMethod2();
           }
       },"线程2").start();
   }

}


**执行结果:**

```html
testMethod1 start:线程1
testMethod1:线程1
testMethod1 end:线程1
testMethod2 start:线程2
testMethod2:线程2
testMethod2 end:线程2
Process finished with exit code 0

从程序执行结果可以看出来,线程1和线程2是顺序执行,也就是说当多个线程访问同一个对象的不同的synchronized方法时,其实获取的是同一把锁,必须等待当前锁的释放。

  • 多个线程访问同一个对象的synchronized方法和非synchronized方法
public class SynchronizedTest{

    public synchronized void testMethod1(){
        System.out.println("testMethod1 start:"+Thread.currentThread().getName());
        try {
            System.out.println("testMethod1:"+Thread.currentThread().getName());
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("testMethod1 end:"+Thread.currentThread().getName());
    }

    public void testMethod2(){
        System.out.println("testMethod2 start:"+Thread.currentThread().getName());
        try {
            System.out.println("testMethod2:"+Thread.currentThread().getName());
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("testMethod2 end:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.testMethod1();
            }
        },"线程1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.testMethod2();
            }
        },"线程2").start();
    }
}

执行结果:

testMethod1 start:线程1
testMethod2 start:线程2
testMethod2:线程2
testMethod1:线程1
testMethod1 end:线程1
testMethod2 end:线程2
Process finished with exit code 0

从结果可以看出线程1和线程2交叉执行,说明了访问synchronized方法得时候可以访问非synchronized方法。

  • 多个线程作用在不同对象上
public class SynchronizedTest{


    public void testMethod1(){
        System.out.println("testMethod1 start:"+Thread.currentThread().getName());
        try {
            System.out.println("testMethod1:"+Thread.currentThread().getName());
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("testMethod1 end:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SynchronizedTest().testMethod1();
            }
        },"线程1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SynchronizedTest().testMethod1();
            }
        },"线程2").start();
    }
}

执行结果:

testMethod1 start:线程1
testMethod1:线程1
testMethod1 start:线程2
testMethod1:线程2
testMethod1 end:线程1
testMethod1 end:线程2
Process finished with exit code 0

上面是两个线程操作了2个对象是不同的锁,执行结果也可以看出线程1和线程2是交叉执行,所以线程之间各不影响。

作用在静态方法

public class SynchronizedTest implements Runnable{

    static int i = 0;

    /**
     * 修饰实例方法
     */
    public static synchronized void increase(){
        System.out.println("测试:"+Thread.currentThread().getName()+",i="+i++);
    }

    @Override
    public void run() {
        for(int j = 0;j < 5; j++){
            increase();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new SynchronizedTest());
        Thread t2 = new Thread(new SynchronizedTest());
        t1.setName("线程1");
        t1.start();
        t1.join();
        t2.setName("线程2");
        t2.start();
        t2.join();
        System.out.println(i);
    }
}

执行结果:

测试:线程1,i=0
测试:线程1,i=1
测试:线程1,i=2
测试:线程1,i=3
测试:线程1,i=4
测试:线程2,i=5
测试:线程2,i=6
测试:线程2,i=7
测试:线程2,i=8
测试:线程2,i=9
10
Process finished with exit code 0

线程1和线程2实例化两个不同的对象,但是访问的是静态方法,但两个线程发生了互斥,因为静态方法是依附于类而不是对象的,当synchronized修饰静态方法时,锁是class对象。

作用在代码块

public class SynchronizedTest implements Runnable{

    private static SynchronizedTest synchronizedTest = new SynchronizedTest();

    static int i = 0;

    /**
     * 修饰实例方法
     */
    public void increase(){
        synchronized (this) {
            System.out.println("测试:" + Thread.currentThread().getName() + ",i=" + i++);
        }
    }

    @Override
    public void run() {
        for(int j = 0;j < 5; j++){
            increase();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(synchronizedTest);
        Thread t2 = new Thread(synchronizedTest);
        t1.setName("线程1");
        t1.start();
        t1.join();
        t2.setName("线程2");
        t2.start();
        t2.join();
        System.out.println(i);
    }
}

执行结果:

测试:线程1,i=0
测试:线程1,i=1
测试:线程1,i=2
测试:线程1,i=3
测试:线程1,i=4
测试:线程2,i=5
测试:线程2,i=6
测试:线程2,i=7
测试:线程2,i=8
测试:线程2,i=9
10
Process finished with exit code 0

代码块的实现方式比方法级别的锁的粒度更小,有利于提高程序的性能。

总结

在这里插入图片描述


版权声明:本文为qq_16438883原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。