浅谈synchronized

    xiaoxiao2022-07-06  144

    synchronized关键字最主要有以下3种应用方式,下面分别介绍

        修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁

        修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁

        修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

    1、修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁

    package com.wangyong.learnproject.synchroniz; public class SynchronizedClass { private String instanceName; public SynchronizedClass() { } public SynchronizedClass(String instanceName) { this.instanceName = instanceName; } public synchronized void methodAAA(Long threadId){ System.out.println("线程编号:"+threadId+"\t方法锁 methodAAA 测试 对象名 start:"+instanceName); //…. try { Thread.sleep(10000L); }catch (Exception e){ e.printStackTrace(); } System.out.println("线程编号:"+threadId+"\t方法锁 methodAAA 测试 对象名 end:"+instanceName); } } package com.wangyong.learnproject.synchroniz; public class SynchronizedTest { // 测试方法同步 public static void testMethodSynechronized(){ final SynchronizedClass p1 = new SynchronizedClass("p1"); final SynchronizedClass p2 = new SynchronizedClass("p2"); Thread thread1 = new Thread(){ public void run() { p1.methodAAA(Thread.currentThread().getId()); }; }; Thread thread2 = new Thread(){ public void run() { p2.methodAAA(Thread.currentThread().getId()); }; }; thread1.start(); thread2.start(); final SynchronizedClass p3 = new SynchronizedClass("p3"); Thread thread1 = new Thread(){ public void run() { p3.methodAAA(Thread.currentThread().getId()); }; }; Thread thread2 = new Thread(){ public void run() { p3.methodAAA(Thread.currentThread().getId()); }; }; thread3.start(); thread4.start(); } public static void main(String []args){ testMethodSynechronized(); //testCodeSynechronized(); //testObjectLockSynechronized(); } }

    运行结果:

    线程编号:12 方法锁 methodAAA 测试 对象名 start:p1 线程编号:15 方法锁 methodAAA 测试 对象名 start:p3 线程编号:13 方法锁 methodAAA 测试 对象名 start:p2 线程编号:13 方法锁 methodAAA 测试 对象名 end:p2 线程编号:12 方法锁 methodAAA 测试 对象名 end:p1 线程编号:15 方法锁 methodAAA 测试 对象名 end:p3 线程编号:14 方法锁 methodAAA 测试 对象名 start:p3 线程编号:14 方法锁 methodAAA 测试 对象名 end:p3

    上述代码中,我们开启了4个线程,两张场景进行测试synchronized的运行结果,

    发现在线程编号为12、13两个线程同时访问同一个类的两个实例对象时,发现对于synchronized 修饰的methodAAA实例方法是没有生成同步效果。因此我们发现当多个线程访问同一个类的不同实例对象的synchronized 修饰的实例方法时,这两个线程访问的资源不是同一个资源(同一对象),而synchronized 锁修改的实例方是基于对象同步操作权限控制的,每一个对象在同一时刻只允许被一个线程访问。因此变成产生了线程编号为12、13的两个线程同时访问p1和p2对象的时候,这两个线程没有产生同步操作的原子性的效果,而线程编号为14、15的两个线程是访问的同一个共享资源对象,所以线程14、15在访问同一个资源p3的时候是同步进行操作的。

    因此:在synchronized 修饰的实例方法中存在两种意思及:

    多个线程访问同一对象资源的时候,产生线程安全是有保障的,其资源的访问也是线程安全的

    而多个线程访问不同对象资源的时候,不会产生线程安全是有保障的,其资源的访问也不是线程安全的。在将其特性衍生到,java的业务场景中时,当我们使用synchronized 修饰的访问非静态方法,且类没有进行单例可控制的时候,在我们使用I/O进行数据操作的时候,由于其底层进行的操作是多线程访问不同的实例对象,因此在进行I/O读写操作的时候不是线程安全的

    2、 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁

    package com.wangyong.learnproject.synchroniz; public class SynchronizedClass { private String instanceName; public SynchronizedClass() { } public SynchronizedClass(String instanceName) { this.instanceName = instanceName; } public synchronized static void staticMethodAAA(Long threadId){ System.out.println("线程编号:"+threadId+"\t方发锁 methodAAA 测试 静态方法同步锁"); //…. try { Thread.sleep(10000L); }catch (Exception e){ e.printStackTrace(); } System.out.println("线程编号:"+threadId+"\t方发锁 methodAAA 测试 静态方法同步锁"); } } package com.wangyong.learnproject.synchroniz; public class SynchronizedTest { // 测试静态方法同步 public static void testStaticMethodSynechronized(){ Thread thread1 = new Thread(){ public void run() { SynchronizedClass.staticMethodAAA(Thread.currentThread().getId()); }; }; Thread thread2 = new Thread(){ public void run() { SynchronizedClass.staticMethodAAA(Thread.currentThread().getId()); }; }; thread1.start(); thread2.start(); } public static void main(String []args){ //testMethodSynechronized(); testStaticMethodSynechronized(); //testCodeSynechronized(); //testObjectLockSynechronized(); } }

    运行结果:

    线程编号:12 方法锁 methodAAA 测试 静态方法同步锁 线程编号:12 方法锁 methodAAA 测试 静态方法同步锁 线程编号:13 方法锁 methodAAA 测试 静态方法同步锁 线程编号:13 方法锁 methodAAA 测试 静态方法同步锁

    当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁

    3、修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

    package com.wangyong.learnproject.synchroniz; public class SynchronizedClass { private String instanceName; public SynchronizedClass() { } public SynchronizedClass(String instanceName) { this.instanceName = instanceName; } //(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。 //――那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制, //造成数据混乱:( public void methodaaa(Long threadId){ // 作用类似于 methodAAA synchronized (this) // (1) { System.out.println("线程编号:"+threadId+"\t代码块锁 methodaaa 测试 对象名 start:"+instanceName); //…. try { Thread.sleep(5000L); }catch (Exception e){ e.printStackTrace(); } System.out.println("线程编号:"+threadId+"\t代码块锁 methodaaa 测试 对象名 end:"+instanceName); } } } package com.wangyong.learnproject.synchroniz; public class SynchronizedTest { // 测试代码块同步 public static void testCodeSynechronized(){ final SynchronizedClass p1 = new SynchronizedClass("p3"); final SynchronizedClass p2 = new SynchronizedClass("p4"); Thread thread1 = new Thread(){ public void run() { p1.methodaaa(Thread.currentThread().getId()); }; }; Thread thread2 = new Thread(){ public void run() { p2.methodaaa(Thread.currentThread().getId()); }; }; thread1.start(); thread2.start(); } public static void main(String []args){ //testMethodSynechronized(); testStaticMethodSynechronized(); //testCodeSynechronized(); //testObjectLockSynechronized(); } }

    运行结果:

    线程编号:12 代码块锁 methodaaa 测试 对象名 start:p3 线程编号:13 代码块锁 methodaaa 测试 对象名 start:p4 线程编号:12 代码块锁 methodaaa 测试 对象名 end:p3 线程编号:13 代码块锁 methodaaa 测试 对象名 end:p4

    处的this指的是什么呢?它指的就是调用这个方法的对象,如P3。可见同步方法实质是将synchronized作用于object reference。 那个拿到了P3对象锁的线程,才可以调用P1的同步方法,而对P4而言,P3这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制, 造成数据混乱

    其作用效果与synchronized的实例方法类似

    最新回复(0)