两个线程同时访问同一个对象的synchronized(this)同步代码块时,在代码运行期间只能有一个线程执行该段代码块,另一个线程必须等待当前线程完成执行才能够执行该段代码。
使用上面这句话完成的例子带来的效率也没有提高,原因在于同步代码块中还是包含了所有的代码,包括线程安全和非线程安全的代码。
当一个线程访问object中的synchronized同步代码块时,其他线程可以访问该object对象中非synchronized(this)同步代码块的内容。
根据上面那句话对同步代码块的优化方案就是,将线程安全且耗时的代码放在同步代码块外面,将非线程安全的代码放在同步代码快中,利用多线程的“同时性”(并发性)实现缩短时间提高效率,且保持当前对象持有锁,避免非线程安全的问题出现。
注:不在synchronized代码块中的代码是异步的,在synchronized中的代码是同步的。
书上通过例子为我们表现了什么叫一半异步一半同步(例子中被执行代码中有一个for循环输出是异步的,还有一个for循环输出是在synchronized中是同步)。
若一个线程访问了object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有的其他synchronized(this)同步代码块的访问将被阻塞。
这个现象表明了:synchronized使用的是一个对象监视器(此处用的是this)。
随即产生了疑惑:synchronized(this)/synchronized(非this对象)与同步方法是否同步,于是做了测试。
synchronized(this)与当前对象的同步方法是同步的,两者公用一把对象锁。
synchronized(非this对象)与当前对象的同步方法是异步的,两者不共用一把对象锁。
synchronized修饰代码块时与synchronized修饰方法时是一样的都是锁定当前对象(对当前对象加锁)。
同我们上一小节的结论一样,当前对象的同步方法与synchronized(this)公用一把对象锁(当前对象),执行的过程是同步的。
多个线程调用同一个对象的synchronized同步方法或者synchronized(this)同步代码块时,调用的方法时按顺序执行的,是同步的是阻塞的。
说明同一个对象的synchronized同步方法与synchronized(this)代码块的对象锁(当前对象锁)有两个用途。
若已经有线程获得对象锁,对其他synchronized同步方法或synchronized(this)同步代码块调用起阻塞作用(即该线程的代码执行是按照同步代码的调用顺序完成的)。
同一时间只有一个线程可以执行synchronized修饰的隔离区内的代码。
注:第一条是针对一个线程来说的,第二条是针对多条线程来说的。
synchronized的对象监视器除了可以使用this外,还可以使用任意对象作为“对象监视器”来实现同步功能,这个“任意对象”大多数是实例变量或方法参数(实际工作中个人认为单例应用的应该比较多)。使用格式:synchronized(非this对象)。
synchronized(非this对象X)格式的作用只有1种:
在多个线程持有“对象监视器”为同一个对象的前提下,一时间只有一个线程可以执行synchronized(非this对象X)同步代码块中的代码。
锁非this对象的优点:如果一个类中有很多synchronized方法,虽然能够保证同步,但是一定会收到阻塞,会影响程序的效率,如果使用synchronized(非this对象),则synchronized(非this对象)与synchronized(this)与synchronized方法是异步的,不必争抢this锁,可以根据项目的构建实现运行效率的优化。
注:同步代码块放在非同步synchronized方法中进行声明,并不能保证调用方法的线程的执行权同步/顺序性。线程的调用是无序的,但是同步代码块执行的顺序是同步的,这样容易出现脏读(i++与println()出现脏读就是这个原因)。
三个结论:
当多个线程同时执行synchronized(x){}同步代码块时呈同步效果
当其他线程执行x对象中的synchronized同步方法时呈同步效果
当其他线程执行x对象中的synchronized(this)方法时也呈现同步效果