Synchronized

185 阅读5分钟

synchronized用来修饰方法及代码块,synchronized代码块相较于synchronized方法更加灵活、且效率有所提升
synchronized关键字,它取得的锁是当前对象,而不是某一段code;且一个对象只有一个锁。深刻的理解synchronized锁定的对象,可以很好的在实践中使用多线程

public synchronized void methodA() {   

}

public void methodB() {
    synchronized (this) { //this-->调用该方法的对象
        
    }
}
methodA与methodB达到的效果是一致,其锁定的是该类的对象

public class ThreadTest {
    public synchronized static void methodE() {

    }

    public void methodF() {
        synchronized (ThreadTest.class) {
        
        }
    }
}
methodE与methodF达到的效果一致,其锁定的是该类的class;
ThreadTest.class 与xxx.getClass()用于同步锁的话,效果不一致

//节省资源
private byte[] lock = new byte[0];

public void methodY() {
    synchronized (lock) {
        
    }
}

引用网上的一个例子:

OBJ就类似于一个大房子,大门永远打开。房子里有很多房间(也就是方法)这些房间有上锁的(synchronized方法)和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。另外所有想调用该对象方法的线程比喻成想进入这房子某个房间的人。

在此先来明确一下前提条件,该对象至少有一个synchronized方法否则key将无意义。 一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时还没有其他人要使用上锁的房间)。于是他拿到了钥匙并且按照自己的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连续使用两间上锁的房间,中间他也要把钥匙还回去,再取回来。因此普通情况下钥匙的使用原则是“随用随借,用完即还

这时其他人可以不受限制的使用那些不上锁的房间,一个人用一间可以,两个人用一间也可以没限制。但是如果当某个人想要进入上锁的房间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话就只能等了。要是很多人在等这把钥匙,等钥匙还回来以后,谁会优先得到钥匙?无法保证。象前面例子里那个想连续使用两个上锁房间的家伙,他中间还钥匙的时候如果还有其他人在等钥匙,那么没有任何保证这家伙能再次拿到。

同步代码块与同步方法有小小的不同1.从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。2.同步代码块还可以人为的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开这个屏风的锁,你可以用本房的钥匙;你也可以指定用另一个房子的钥匙才能开,这样的话,你要跑到另一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。

     你获得的另一栋房子的钥匙并不影响其他人进入那栋房子没有锁的房间。为什么要使用同步代码块?首先对程序来讲同步的部分很影响运行效率,而一个方法通常是先创建一些局部变量,再对这些变量做一些操作,如运算,显示等;而同步所覆盖的代码越多,对效率的影响就越严重。因此我们通常尽量缩小其影响范围。

如何做?我们只把一个方法中该同步的地方同步,比如运算。

同步代码块可以指定钥匙这一特点有个额外的好处,可以在一定时期内霸占某个对象的key。记得前面说过普通情况下钥匙的使用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永远不还,而是在退出同步代码块时才还。还用前面那个想连续用两个上锁房间的家伙打比方。怎样才能在用完一间以后,继续使用另一间呢。用同步代码块吧。先创建另外一个线程,做一个同步代码块,把那个代码块的锁指向这个房子的钥匙。然后启动那个线程。只要你能在进入那个代码块时抓到这房子的钥匙,你就可以一直保留到退出那个代码块。也就是说你甚至可以对本房内所有上锁的房间遍历,甚至再sleep(XXX),房门口却还有1K个线程在等这把钥匙呢

最后也许有人会问,为什么要一把钥匙通开,而不是一个钥匙一个门呢?我想这纯粹是因为复杂性问题。一个钥匙一个门当然更安全,但是会牵扯好多问题。钥匙的产生,保管,获得,归还等。其复杂性有可能随同步方法的增加呈几何级数增加,严重影响效率。这也算是一个权衡的问题吧。为了增加一点点安全性,导致效率大大降低,是多么不可取啊。

synchronized规则

  • 并发线程访问同一个对象中的synchronized方法时,单位时间内只能有一个线程可以执行。其他线程必须等待当前线程执行完该synchronized方法后才能执行synchronized方法,并且其他线程必须等待当前线程执行完该synchronized方法后才能执行其他的synchronized方法
  • 当一个线程访问同一个对象中的synchronized方法时,单位时间内其他线程可以访问该对象的非synchronized方法