synchronized 基本规则
1. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
class MyRunable implements Runnable { public void run() { synchronized (this) { // this是指“当前的类对象” 此处为MyRunable对象 demo try { for (int i = 0; i < 5; i++) { Thread.sleep(100); // 休眠100ms System.out.println(Thread.currentThread().getName() + " loop " + i); } } catch (InterruptedException ie) { } } } } public class Demo1_1 { public static void main(String[] args) { Runnable demo = new MyRunable(); // 新建“Runnable对象” Thread t1 = new Thread(demo, "t1"); // 新建“线程t1”, t1是基于demo这个Runnable对象 Thread t2 = new Thread(demo, "t2"); // 新建“线程t2”, t2是基于demo这个Runnable对象 t1.start(); // 启动“线程t1” t2.start(); // 启动“线程t2” } }
===========
t1 loop 0
t1 loop 1
t1 loop 2
t1 loop 3
t1 loop 4
t2 loop 0
t2 loop 1
t2 loop 2
t2 loop 3
t2 loop 4
2. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
3. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
synchronized 方法 和 synchronized 代码块
(假设P1、P2是同一个类的不同对象,类中定义了同步块或同步方法,P1 P2都可以调用它。)
Public synchronized void methodA() { //…. }
等同于
public void methodA() { synchronized (this) // this指的就是调用这个方法的对象,如P1 { //….. } }
同步块:
public void method3(SomeObject so) { synchronized(so) // 锁是so这个对象(当有一个明确的对象作为锁时,可以这样写) { //….. } }
当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable { private byte[] lock = new byte[0]; // 零长度的byte数组充当锁 Public void methodA() { synchronized(lock) {
//…
} } //….. }
synchronized 修饰static方法
Class Foo { public synchronized static void methodA() // 同步的static方法 { //…. } public void methodB() { synchronized(Foo.class) // 锁当前调用这个方法的对象所属的类 } }
methodB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这
个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)
实例锁 和 全局锁
实例锁 -- 锁在某一个实例对象上。如果该类是单例,那么该锁也具有全局锁的概念。 实例锁对应的就是synchronized关键字。
全局锁 -- 该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。 全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。
// 举例
pulbic class Something { public synchronized void isSyncA(){} public synchronized void isSyncB(){} public static synchronized void cSyncA(){} public static synchronized void cSyncB(){} }
假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况。
(1) x.isSyncA()与x.isSyncB() // 不能同时访问,都是锁的对象x
(2) x.isSyncA()与y.isSyncA() // 可以同时访问,锁的不是同一个对象
(3) x.cSyncA()与y.cSyncB() // 不能同时访问,static方法,两个都相当于Something.isSyncA()。锁的同一个类
(4) x.isSyncA()与Something.cSyncA() // 可以同时访问,x.isSyncA()使用的是对象x的锁;而cSyncA()是静态方法,Something.cSyncA()可以理解对使用的是“类的锁”。