zoukankan      html  css  js  c++  java
  • synchronized的理解和应用

    synchronized是什么

    • java内置锁,可以保证方法或者代码块在运行时候,同一时刻只有一个线程进入临界区,同时它还保证了共享变量的可见性(限定同一个临界区,等同于volatile)

    synchronized几种用法

    • 修饰普通方法,锁的是当前实例
    • 修饰代码块,锁的括号里面的对象
    • 修改静态方法,锁的是当前类的class对象

    举个例子

    1. 修饰普通方法
    点击查看代码
    public class SynchNoStaticClass {
        public synchronized void test2() {
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
        public static void main(String[] args) {
            SynchNoStaticClass synchNoStaticClass = new SynchNoStaticClass();
            new Thread(()->{
                synchNoStaticClass.test2();
            },"thread--1").start();
            new Thread(()->{
                synchNoStaticClass.test2();
            },"thread--2").start();
        }
    }
    

    结果:
    image
    分析:不同线程访问同一个对象的临界方法,需要等前一个线程执行完毕后把锁释放才能执行(执行结果是顺序的),如果是多个线程作用的是不同的对象,那么执行结果就可能不是顺序的,因为不同的对象锁,互不影响。

    2. 修饰静态方法
    点击查看代码
    public class TestSynchronized {
        public static  synchronized void test2() {
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
        public void test1(){
            test2();
        }
        public static void main(String[] args) {
    
            new Thread(()-> {
                TestSynchronized testSynchronizedOne = new TestSynchronized();
                testSynchronizedOne.test1();
                }, "thread--1").start();
    
            new Thread(()->{
                TestSynchronized testSynchronizedTwo = new TestSynchronized();
                testSynchronizedTwo.test1();
                },"thread--2").start();
        }
    }
    

    结果:
    image
    分析:不同线程访问不同的对象,但是访问方法是静态的,当前class的对象锁生效,结果就是顺序执行(可以把synchronized去掉,结果是交替输出的)

    3.修饰代码块

    为啥需要修饰代码块呢,因为修饰方法的范围粒度太大了,我们往往需要方法中的某段代码互斥进行同步操作

    点击查看代码
    public class SynchNoStaticClass {
        public static void main(String[] args) {
            SynchNoStaticClass synchNoStaticClass = new SynchNoStaticClass();
            new Thread(() -> {
                synchNoStaticClass.test3();
            }, "test3").start();
            new Thread(() -> {
                synchNoStaticClass.test3();
            }, "test4").start();
        }
        public void test3() {
            synchronized (SynchNoStaticClass.class) {
                int i = 5;
                while (i-- > 0) {
                   System.out.println(Thread.currentThread().getName() + " : " + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ie) {
                    }
                }
            }
        }
    }
    

    结果:
    image
    分析:每次当线程进入synchronized包裹的代码块时就会,就会获取SynchNoStaticClass.class对象锁,那么其他线程在进来需要等待

    synchronized的可重入性

    什么是可重入锁呢?当一个线程再次请求由自己持有的对象锁时候是可以获取成功的,看下面代码

    点击查看代码
    public class SynchDoubleClass {
    
        public synchronized void test2() {
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + "test2:" + i);
            }
            test3();
        }
        public synchronized void test1() {
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + "test1:" + i);
            }
            test2();
        }
        public synchronized void test3() {
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + "test3:" + i);
            }
        }
        public static void main(String[] args) {
            new Thread(()->{
                SynchDoubleClass synchDoubleClass = new SynchDoubleClass();
                synchDoubleClass.test1();
            }).start();
    
        }
    }
    
    结果:

    image

    分析:因为synchronized 锁是可重入的,所以在test1方法没执行完,在去调用test2方法,因为是同一个对象锁,它依然会获得锁。


    synchronized与awit/notify/notifyAll使用(等待通知机制)

    • awit阻塞当前线程,就是释放当前线程的锁
    • notify通知某个阻塞线程,notifyall 通知所有的阻塞线程

    synchronized的底层实现

    • 字节码反编译结果
      image
      分析:
    • 先说结论,修饰方法JVM通过ACC_SYNCHRONIZED标识实现,修饰代码块,JVM通过 monitorenter和monitorexit两个指令实现同步,而monitor(对象监视器)的本质依赖于底层操作系统的 互斥锁(Mutex Lock) 实现,
    • 关于class文件结构以及更底层的c++文件源码解读,当前笔者还是小白,无法更深入了解,留待后续补充
  • 相关阅读:
    Debate
    图形算法
    OpenGL Notes
    How to Write an Ethics Paper
    Thesis
    addWindowListener -> WindowAdapter -> windowClosing
    Thesis
    Bootcamp: An error occurred while partitioning the disk
    What Is XML Schema
    What Is XML
  • 原文地址:https://www.cnblogs.com/tycellwu/p/15816041.html
Copyright © 2011-2022 走看看