Synchronized基本用法、性质和原理

Synchronized的两种用法

  • Synchronized作用:能够保证在同一时刻最多只有一个线程执行该代码,以达到保证并发安全的效果

  • 对象锁

    • 方法锁:Synchronized修饰普通方法,默认锁对象为this当前实例对象
    • 同步代码块锁:手动指定锁对象
  • 类锁

    • Synchronized修饰静态的方法
    • 指定锁为Class对象,类锁只能在同一时刻被一个对象拥有

多线程访问同步方法的7种情况

  • 两个线程同时访问一个对象的同步方法

    争抢同一把锁,相互等待

    public class SynchronizedObjectMethod3 implements Runnable {
          
          
        static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
    
            t1.start();
            t2.start();
    
            while (t1.isAlive() || t2.isAlive()){
          
          
    
            }
    
            System.out.println("finished");
        }
    
        @Override
        public void run() {
          
          
            method();
        }
    
        public synchronized void method(){
          
          
            System.out.println("对象锁的普通方法形式:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(3000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    }
    
  • 两个线程访问的是两个对象的同步方法

    争抢不同的锁,并发执行

    public class SynchronizedObjectCodeBlock2 implements Runnable {
          
          
        static SynchronizedObjectCodeBlock2 instance1 = new SynchronizedObjectCodeBlock2();
        static SynchronizedObjectCodeBlock2 instance2 = new SynchronizedObjectCodeBlock2();
        Object lock1 = new Object();
        Object lock2 = new Object();
    
        @Override
        public void run() {
          
          
            synchronized (this) {
          
          
                System.out.println("对象锁的代码块形式:"+ Thread.currentThread().getName());
                try {
          
          
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":lock1运行结束");
            }
        }
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(instance1);
            Thread t2 = new Thread(instance2);
    
            t1.start();
            t2.start();
    
            while(t1.isAlive() || t2.isAlive()){
          
          
    
            }
    
            System.out.println("finished");
        }
    }
    
  • 两个线程访问的是synchronized的静态方法

    争抢同一把锁,相互等待

    public class SynchronizedClassStatic4 implements Runnable {
          
          
        static SynchronizedClassStatic4 instance1 = new SynchronizedClassStatic4();
        static SynchronizedClassStatic4 instance2 = new SynchronizedClassStatic4();
    
        @Override
        public void run() {
          
          
            method();
        }
    
        public synchronized static void method(){
          
          
            System.out.println("类锁的静态方法形式:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(3000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(instance1);
            Thread t2 = new Thread(instance2);
    
            t1.start();
            t2.start();
    
            while (t1.isAlive() || t2.isAlive()){
          
          
    
            }
    
            System.out.println("finished");
        }
    }
    
  • 同时访问同步方法与非同步方法

    非同步方法不受影响,同步方法与非同步方法并发执行

    public class SynchronizedYesAndNo6 implements Runnable {
          
          
        static SynchronizedYesAndNo6 instance = new SynchronizedYesAndNo6();
        @Override
        public void run() {
          
          
            if (Thread.currentThread().getName().equals("Thread-0")){
          
          
                method1();
            } else {
          
          
                method2();
            }
        }
    
        public synchronized void method1(){
          
          
            System.out.println("加锁的方法:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public void method2(){
          
          
            System.out.println("未加锁的方法:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
    
            t1.start();
            t2.start();
    
            while (t1.isAlive() || t2.isAlive()){
          
          
    
            }
    
            System.out.println("finished");
        }
    }
    
  • 访问同一个对象的不同的普通同步方法

    相互等待,默认为this对象,争抢同一把锁

    public class SynchronizedDifferentMethod7 implements Runnable {
          
          
        static SynchronizedDifferentMethod7 instance = new SynchronizedDifferentMethod7();
        @Override
        public void run() {
          
          
            if (Thread.currentThread().getName().equals("Thread-0")){
          
          
                method1();
            } else {
          
          
                method2();
            }
        }
    
        public synchronized void method1(){
          
          
            System.out.println("加锁的方法1:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public synchronized void method2(){
          
          
            System.out.println("加锁的方法2:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
    
            t1.start();
            t2.start();
    
            while (t1.isAlive() || t2.isAlive()){
          
          
    
            }
    
            System.out.println("finished");
        }
    }
    
  • 同时访问静态synchronized和非静态synchronized方法

    并发执行,静态方法使用类锁,非静态方法使用对象锁

    public class SynchronizedStaticAndNormal8 implements Runnable {
          
          
        static SynchronizedStaticAndNormal8 instance = new SynchronizedStaticAndNormal8();
        @Override
        public void run() {
          
          
            if (Thread.currentThread().getName().equals("Thread-0")){
          
          
                method1();
            } else {
          
          
                method2();
            }
        }
    
        public synchronized static void method1(){
          
          
            System.out.println("加锁的静态方法:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public synchronized void method2(){
          
          
            System.out.println("加锁的方法普通:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
    
            t1.start();
            t2.start();
    
            while (t1.isAlive() || t2.isAlive()){
          
          
    
            }
    
            System.out.println("finished");
        }
    }
    
  • 方法抛异常后,会释放锁

    一个同步方法抛异常后,jvm自动释放锁;锁可以被下一个同步方法获取到

    public class SynchronizedException9 implements Runnable {
          
          
        static SynchronizedException9 instance = new SynchronizedException9();
        @Override
        public void run() {
          
          
            if (Thread.currentThread().getName().equals("Thread-0")){
          
          
                method1();
            } else {
          
          
                method2();
            }
        }
    
        public synchronized void method1(){
          
          
            System.out.println("加锁的方法1:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            throw new RuntimeException();
        }
    
        public synchronized void method2(){
          
          
            System.out.println("加锁的方法2:" + Thread.currentThread().getName());
            try {
          
          
                Thread.sleep(2000);
            } catch (InterruptedException e) {
          
          
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    
        public static void main(String[] args) {
          
          
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
    
            t1.start();
            t2.start();
    
            while (t1.isAlive() || t2.isAlive()){
          
          
    
            }
    
            System.out.println("finished");
        }
    }
    

Synchronized的性质

  • 可重入:同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁;避免死锁,提高封装性;粒度是线程而非调用
    1. 同一个方法是可重入的

      public class SynchronizedRecursion10 {
              
              
      
          int i = 0;
      
          public static void main(String[] args) {
              
              
              SynchronizedRecursion10 instance = new SynchronizedRecursion10();
              instance.method1();
          }
      
          private synchronized void method1(){
              
              
              System.out.println("method1: " + i);
              if (i == 0){
              
              
                  i++;
                  method1();
              }
          }
      }
      
    2. 可重入不要求是同一个方法

      public class SynchronizedOtherMethod11 {
              
              
          private synchronized void method1(){
              
              
              System.out.println("method1");
              method2();
          }
      
          private synchronized void method2(){
              
              
              System.out.println("method2");
          }
      
          public static void main(String[] args) {
              
              
              SynchronizedOtherMethod11 s = new SynchronizedOtherMethod11();
              s.method1();
          }
      }
      
    3. 可重入不要求是同一个类中的

      public class SynchronizedSuperClass12 {
              
              
          public synchronized void doSomething(){
              
              
              System.out.println("父类方法");
          }
      }
      
      class TestClass extends SynchronizedSuperClass12{
              
              
          @Override
          public synchronized void doSomething(){
              
              
              System.out.println("子类方法");
              super.doSomething();
          }
      
          public static void main(String[] args) {
              
              
              TestClass t = new TestClass();
              t.doSomething();
          }
      }
      
  • 不可中断:一旦锁被其他线程获得,只能选择等待或者阻塞,直到别的线程释放锁

Synchronized的原理

  • 加锁和释放锁的原理

    synchronized会自动解锁,而Lock要手动解锁,效果一样

    public class SynchronizedToLock13 {
          
          
        Lock lock = new ReentrantLock();
    
        public synchronized void method1(){
          
          
            System.out.println("Synchronized形式的锁");
        }
    
        public void method2(){
          
          
            lock.lock();
            try {
          
          
                System.out.println("lock形式的锁");
            } finally {
          
          
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
          
          
            SynchronizedToLock13 s = new SynchronizedToLock13();
            s.method1();
            s.method2();
        }
    }
    
  • 可重入性原理

    1. JVM负责跟踪对象被加锁的次数
    2. monitorenter表示加锁,monitorexit表示解锁
    3. 线程第一次给对象加锁的时候,计数变为1。每当这个相同的线程在此基础上再次获得锁时,计数会递增
    4. 每当任务离开时,计数递减,当计数为0时,锁完全被释放
    public class Decompilation14 {
          
          
        private Object object = new Object();
    
        public void insert(Thread thread){
          
          
            synchronized (object){
          
          
    
            }
        }
    
        public static void main(String[] args) {
          
          
            Decompilation14 decompilation14 = new Decompilation14();
        }
    }
    

    在这里插入图片描述

  • 可见性原理
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44863537/article/details/112056083