欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于synchronized的那些事

程序员文章站 2022-04-15 18:26:21
关于synchronized的那些事一、简介二、使用1、对象锁-同步代码块2、对象锁-方法锁3、类锁-static方法锁4、类锁-class锁三、使用总结四、性质1、可重入性2、不可中断性质五、缺陷一、简介Synchronized一句话来解释其作用就是:能够保证同一时刻最多只有一个线程执行该段代码,以达到并发安全的效果。也就是说Synchronized就好比是一把锁,某个线程把资源锁住了之后,别人就不能使用了,只有当这个线程用完了别人才能用。对于Synchronized关键字来说,它是并发编程中一个元老...

一、简介

Synchronized一句话来解释其作用就是:能够保证同一时刻最多只有一个线程执行该段代码,以达到并发安全的效果。也就是说Synchronized就好比是一把锁,某个线程把资源锁住了之后,别人就不能使用了,只有当这个线程用完了别人才能用。对于Synchronized关键字来说,它是并发编程中一个元老级角色,也就是说你只要学习并发编程,就必须要学习Synchronized关键字。由此可见其地位。

二、使用

对于synchronized关键字来说,一共可以分为两类:对象锁和类锁。
关于synchronized的那些事

1、对象锁-同步代码块

对象锁-同步代码块:表示同一时刻只有一个线程能够进入代码块

public class SynchronizedCode {
    public static void main(String[] args) {
        SynTest01 synTest01 = new SynTest01();
        Thread thread1 = new Thread(() -> synTest01.test(), "A");
        Thread thread2 = new Thread(() -> synTest01.test(), "B");
        thread1.start();
        thread2.start();
    }
}
class SynTest01 {
    Object object = new Object();
    public void test() {
        // 该段代码位于同步代码块之外,不会受锁的限制,任何线程随时随地都可以执行当前这段代码
        System.out.println("线程-" + Thread.currentThread().getName() + "进入test方法!");
        synchronized (object) {
            try {
                System.out.println(Thread.currentThread().getName() + "开始执行test方法");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "执行test方法完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

关于synchronized的那些事
当分别两个不同的对象锁不同的代码块的时候,执行步骤:
假如此时有两个线程-A、B

  • 1、A先获取object1锁,执行代码块-1,B进来后只能等待;
  • 2、A执行完代码块-1后释放object1,B才能获取资源并执行代码块-1,但是A会同时去获取object2并执行代码块-2;
  • 3、A执行完代码块-2,释放object2,B执行完代码块-1,获取object-2,继续执行代码块-2。
public class SynchronizedCode {
    public static void main(String[] args) {
        SynTest02 synTest02 = new SynTest02();
        Thread thread1 = new Thread(() -> synTest02.test(), "A");
        Thread thread2 = new Thread(() -> synTest02.test(), "B");
        thread1.start();
        thread2.start();
    }
}
class SynTest02 {
    Object object1 = new Object();
    Object object2 = new Object();

    public void test() {
        // 代码块-1
        synchronized (object1) {
            try {
                System.out.println(Thread.currentThread().getName() + "开始执行object1方法");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "执行object1方法完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 代码块-2
        synchronized (object2) {
            try {
                System.out.println(Thread.currentThread().getName() + "开始执行object2方法");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "执行object2方法完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

关于synchronized的那些事

2、对象锁-方法锁

相比较同步代码块锁就简单很多了,就是在普通方法上添加synchronized关键字修饰即可。

public class SynchronizedCode {
    public static void main(String[] args) {
        SynTest03 synTest03 = new SynTest03();
        Thread thread1 = new Thread(() -> synTest03.test(), "A");
        Thread thread2 = new Thread(() -> synTest03.test(), "B");
        thread1.start();
        thread2.start();
    }
}
class SynTest03 {
    public synchronized void test() {
        System.out.println("线程-" + Thread.currentThread().getName() + "进入test方法!");
        try {
            System.out.println(Thread.currentThread().getName() + "开始执行test方法");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "执行test方法完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程-" + Thread.currentThread().getName() + "退出test方法!");
    }
}

关于synchronized的那些事
功能与同步代码块相似,但是它的锁对象是this,即当前对象
我们可以通过一下方法进行验证:同一时刻只能一个线程进入同步方法,普通方法不受此限制

public class SynchronizedCode {
    public static void main(String[] args) {
        SynTest04 synTest04 = new SynTest04();
        Thread thread1 = new Thread(() -> synTest04.test1(), "A");
        Thread thread2 = new Thread(() -> synTest04.test2(), "B");
        Thread thread3 = new Thread(() -> synTest04.test3(), "B");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class SynTest04 {
    public synchronized void test1() {
        System.out.println("线程-" + Thread.currentThread().getName() + "进入test1方法!");
        try {
            System.out.println(Thread.currentThread().getName() + "开始执行test1方法");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "执行test1方法完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程-" + Thread.currentThread().getName() + "退出test方法!");
    }

    public synchronized void test2() {
        System.out.println("线程-" + Thread.currentThread().getName() + "进入test2方法!");
        try {
            System.out.println(Thread.currentThread().getName() + "开始执行test2方法");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "执行test2方法完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程-" + Thread.currentThread().getName() + "退出test2方法!");
    }

    public void test3() {
        System.out.println("线程-" + Thread.currentThread().getName() + "进入test3方法!");
        try {
            System.out.println(Thread.currentThread().getName() + "开始执行test3方法");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "执行test3方法完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程-" + Thread.currentThread().getName() + "退出test3方法!");
    }
}

关于synchronized的那些事

3、类锁-static方法锁

在java中,java的类对象可能有无数个,但是类却只有一个,同一时刻只能一个线程执行类下的静态同步方法。

public class SynchronizedCode {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> SynTest05.test1(), "A");
        Thread thread2 = new Thread(() -> SynTest05.test1(), "B");
        thread1.start();
        thread2.start();
    }
}
class SynTest05 {
    public static synchronized void test1() {
        System.out.println("线程-" + Thread.currentThread().getName() + "进入test1静态方法!");
        try {
            System.out.println(Thread.currentThread().getName() + "开始执行test1静态方法");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "执行test1静态方法完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程-" + Thread.currentThread().getName() + "退出test静态方法!");
    }

}

关于synchronized的那些事

4、类锁-class锁

无论多少个对象实例,但是同一时间只能一个线程获取class锁并执行相关的同步代码。

public class SynchronizedCode {
    public static void main(String[] args) {
        SynTest06 synTest06 = new SynTest06();
        Thread thread1 = new Thread(() -> synTest06.test1(), "A");
        Thread thread2 = new Thread(() -> synTest06.test1(), "B");
        thread1.start();
        thread2.start();
    }
}
class SynTest06 {
    public void test1() {
        System.out.println("线程-" + Thread.currentThread().getName() + "进入test1静态方法!");
        synchronized (SynTest06.class) {
            try {
                System.out.println(Thread.currentThread().getName() + "开始执行test1静态方法");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "执行test1静态方法完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程-" + Thread.currentThread().getName() + "退出test静态方法!");
        }
    }
}

关于synchronized的那些事

三、使用总结

关于synchronized的那些事

四、性质

对于synchronized关键字主要有两个性质:可重入性质和不可中断性质。我们分别来看。

1、可重入性

什么是可重入呢?指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁。我们举一个例子来说明,一句话吃着碗里的看着锅里的。嘴里面还没吃完就继续再去拿吃的。这就是可重入。不可重入的意思正好相反,你吃完了这碗饭才能盛下一碗。

可重入的程度可以细分为三种情况,我们分别测试一下:

(1)同一个方法中是可重入的。就好比是递归调用同步方法。

(2)不同的方法是可重入的。就好比是一个同步方法调用另外一个同步方法。

(3)不同的类方法是可重入的。

2、不可中断性质

不可中断的意思你可以这样理解,别人正在打游戏,你也想玩,你必须要等别人不想玩了你才能去。在java中表示一旦这个锁被别人抢走了,你必须等待。等别的线程释放了锁,你才可以拿到。否则就一直等下去。

这一点看起来是个有点但其实在某些场景下弊端超级大,因为假如拿到锁得线程永远的不释放,那你就要永远的等下去。

五、缺陷

synchronized关键字既有优点也有缺点,而且缺点贼多,所以后来出现了比他更好的锁。下面我们就来分析一下。

1、效率低

我们之前曾经分析过synchronized关键字是不可中断的,这也就意味着一个等待的线程如果不能获取到锁将会一直等待,而不能再去做其他的事了。

这里也说明了对synchronized关键字的一个改进措施,那就是设置超时时间,如果一个线程长时间拿不到锁,就可以去做其他事情了。
2、不够灵活

加锁和解锁的时候,每个锁只能有一个对象处理,这对于目前分布式等思想格格不入。

3、无法知道是否成功获取到锁

也就是我们的锁如果获取到了,我们无法得知。既然无法得知我们也就很不容易进行改进。

既然synchronized有这么多缺陷。所以才出现了各种各样的锁。

本文地址:https://blog.csdn.net/qq_40722604/article/details/109624829

相关标签: JAVA