简单的synchronized,一句话概括“确保在同一时刻只有一个线程访问该段代码”

synchronized的学习

        1、synchronized的用处,确保一个时刻只有一个线程可以访问该代码

                确保一个时刻只有一个线程可以访问该代码

                解决的问题,举例:

                创建两个线程thred1和thred2,分别调用method1将全局变量i加十万次,不加线程锁,每次都会小于20万次,原因是因为每次线程拿到的可能不是最新的数据,比如thread1可能还没将i加到2,thread2此时获取到的i不是最新的,也就造成了数据错误,              这也就是我们此次要解决的问题

    static Integer i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> { method1();}, "thread1");

        Thread thread2 = new Thread(() -> {method1();}, "thread2");
        thread1.start();
        thread2.start();

        //join等待执行完再执行打印
        thread1.join();
        thread2.join();
        System.out.println("此时i等于:" + i);

    }

    private static void method1() {
        for(int j = 0;j<100000;j++){
            i = i + 1;
        }
    }

                       

        2、synchronized的使用方式

                    1、同步代码块,synchronized (this), 注意一点,这种用法前提是方法为普通方法

       Runnable runnable = new Runnable() {
            @Override
            public void run() {
                //同步方法块需要方法为普通方法,静态方法不可加this
                synchronized (this){
                    for(int j = 0;j<100000;j++){
                        i = i + 1;
                    }
                }
            }
        };

        Thread thread1 = new Thread(runnable, "thread1");

        Thread thread2 = new Thread(runnable, "thread2");
        thread1.start();
        thread2.start();

        //join等待执行完再执行打印
        thread1.join();
        thread2.join();
        System.out.println("此时i等于:" + i);

                 2、类方法块, synchronized (Object.class), 即使是new多个对象访问这一个方法,也是起作用的

    static Integer i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() ->{new ThreadDemo1().method1();}, "thread1");

        Thread thread2 = new Thread(() ->{new ThreadDemo1().method1();}, "thread2");
        thread1.start();
        thread2.start();

        //join等待执行完再执行打印
        thread1.join();
        thread2.join();
        System.out.println("此时i等于:" + i);

    }

    public static void method1() {
        synchronized (ThreadDemo1.class){
            for(int j = 0;j<100000;j++){
                i = i + 1;
            }
        }
    }

            3、普通方法块,必须为同一个类中的同一个方法,如果new一个新的类再去调用,锁就不起作用

     Runnable runnable = new Runnable() {
            @Override
            public synchronized void run() {
                //同步方法块需要方法为普通方法,静态方法不可加this
                    for(int j = 0;j<100000;j++){
                        i = i + 1;
                    }
            }
        };

        Thread thread1 = new Thread(runnable, "thread1");

        Thread thread2 = new Thread(runnable, "thread2");
        thread1.start();
        thread2.start();

        //join等待执行完再执行打印
        thread1.join();
        thread2.join();
        System.out.println("此时i等于:" + i);

          4、静态方法块  ,即使new多个对象,访问静态方法块锁也会起作用

    static Integer i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> { method1();}, "thread1");

        Thread thread2 = new Thread(() -> {method1();}, "thread2");
        thread1.start();
        thread2.start();

        //join等待执行完再执行打印
        thread1.join();
        thread2.join();
        System.out.println("此时i等于:" + i);

    }

    private static synchronized void method1() {
        for(int j = 0;j<100000;j++){
            i = i + 1;
        }
    }

   3、方法块使用总结

             如果你认真看了以上代码,下面总结一下

             1、静态方法锁类锁都是即使new多个对象,只要访问该方法,锁就会起作用。这是因为类锁和静态方法锁都是在系统启动时就加了锁,这块被锁住的方法一直也只会有一个锁

             2、普通方法锁同步代码块都是只作用于当前,如果再有new一个新对象,就是重新的一个锁

             3、当方法抛出异常,会直接释放锁,被下一个线程调用

   4、synchronized的特性

             1、互斥性(这名字起的真高大上):其实就是保证方法在一个时刻只能被一个线程所调用,只有把锁释放时,才可以被其他线程调用

             2、可重入性:当拿到锁的使用权时,可以再次调用该锁。

                   好处:避免死锁,其实就是因为锁的特性是互斥性的,必须先释放锁然后才能被其他锁调用,如果没有可重入性,锁还被自己调用着呢,就不让自己再调用了,那就死锁了

                   举例:thread1调用method1获取到了ThreadDemo1.class这个锁,又去调用了method2再次拿了ThreadDemo1.class这个锁,也就证明了可重入性

    static Integer i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            new ThreadDemo1().method1();
        }, "thread1");
        thread1.start();

        //join等待执行完再执行打印
        thread1.join();
        System.out.println("此时i等于:" + i);

    }

    private void method1() {
        synchronized (ThreadDemo1.class) {
            for (int j = 0; j < 100000; j++) {
                i = i + 1;
            }
        }

        method2();
    }

    private void method2() {
        synchronized (ThreadDemo1.class) {
            for (int j = 0; j < 100000; j++) {
                i = i + 1;
            }
        }

    }

   5、死锁的理解 

          两个线程已经在外层获取到了各自的锁,再去内层获取对方锁的时候,发现被对方占用,也就造成了死锁

          代码解释:线程1调用方法1得到了锁1,线程2调用方法2得到了锁2,线程1再去调用锁2时发现锁2被线程2锁着呢,也就调用不了,同理,线程2再去调用锁1时发现锁1被线程1锁着呢

 public static final String lock1 = "lock1";
    public static final String lock2 = "lock2";

    public static void main(String[] ars) {
        Thread thread1 = new Thread(() -> {method1();}, "线程1");
        Thread thread2 = new Thread(() -> {method2();}, "线程2");
        //两个线程已经在外层获取到了各自的锁,再去内层获取对方锁的时候,发现被对方占用,也就造成了死锁
        thread1.start();
        thread2.start();
    }

    private static void method1() {
        try{
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + "启动");
            while(true){
                synchronized(lock1){
                    System.out.println(thread.getName() + "拿到了lock1锁");
                    //等待时间让线程锁住
                    Thread.sleep(100);
                    System.out.println(thread.getName() + "要去锁lock2锁");
                    synchronized(lock2){
                        System.out.println(thread.getName() + "要去锁lock2锁");
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private static void method2() {
        try{
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + "启动");
            while(true){
                synchronized(Demo1.lock2){
                    System.out.println(thread.getName() + "拿到了lock2锁");
                    //等待时间让线程锁住
                    Thread.sleep(100);
                    System.out.println(thread.getName() + "要去锁lock1锁");
                    synchronized(Demo1.lock1){
                        System.out.println(thread.getName() + "要去锁lock1锁");
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_38384460/article/details/111183233
今日推荐