synchronized的原理
在java中synchronized是用来给对象、方法、或者代码块加上同步锁的,通过synchronized可以在多线程中实现对对象、方法、或者代码块的互斥访问。
例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作; 而此时,线程B也企图获取“obj的同步锁” , 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。
synchronized的规则
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
通过代码来验证上面的三条规则:
一
当一个线程访问对象中的synchronized(this)同步代码块时,其他线程对该代码块将阻塞:
static class MyThread implements Runnable{
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public static void main(String[] args) {
Runnable runnable = new MyThread();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
}
打印结果:
Thread-0:0
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-1:4
Thread-1:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
MyThread的run方法中通过synchronized对代码块加了同步锁,然后创建一个runnable对象,线程t1和t2都访问runnable对象并调用它的run方法,可以理解为t1和t2共享了这个对象的同步锁,所以t2必须等到t1执行完成后才能执行。
将上面的代码修改如下:
Runnable runnable = new MyThread();
Runnable runnable1 = new MyThread();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable1);
打印结果:
Thread-1:0
Thread-0:0
Thread-1:1
Thread-0:1
Thread-1:2
Thread-0:2
Thread-1:3
Thread-0:3
Thread-1:4
Thread-0:4
Thread-1:5
Thread-0:5
Thread-1:6
Thread-0:6
Thread-1:7
Thread-0:7
Thread-1:8
Thread-0:8
Thread-1:9
Thread-0:9
虽然加同步锁的是同一个代码块,但因为是访问的不同的对象runnable和runnable1,t1和t2并没有共享同步锁,所以两个线程同步执行。
二
当一个线程访问对象的synchronized代码块时,另一个线程依然可以访问它的非synchronized代码块
static class Dog {
public void running(){
synchronized (this) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("the dog is running:"+i);
}
}
}
public void breaking() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("the dog is breaking:"+i);
}
}
}
public static void main(String[] args) {
Dog dog = new Dog();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dog.running();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
dog.breaking();
}
});
t1.start();
t2.start();
}
打印结果:
the dog is running:0
the dog is breaking:0
the dog is running:1
the dog is breaking:1
the dog is breaking:2
the dog is running:2
the dog is breaking:3
the dog is running:3
the dog is running:4
the dog is breaking:4
the dog is running:5
the dog is breaking:5
the dog is running:6
the dog is breaking:6
the dog is running:7
the dog is breaking:7
the dog is running:8
the dog is breaking:8
the dog is running:9
the dog is breaking:9
上面的代码创建了一个dog对象,它有两个方法一个加了synchronized的running和一个普通的breaking方法。当t1去调用它的running方法时并没有阻塞到t2去调用它没有加同步锁的方法。
三
当一个线程访问对象的加了同步锁的方法A时,另一个线程调用这个对象的另一个加了同步锁的方法B时也会阻塞
将第二条中Dog的breaking方法也加上同步锁如下:
public void breaking() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("the dog is breaking:"+i);
}
}
}
然后依然用两个线程去访问:
Dog dog = new Dog();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dog.running();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
dog.breaking();
}
});
t1.start();
t2.start();
打印结果:
the dog is running:0
the dog is running:1
the dog is running:2
the dog is running:3
the dog is running:4
the dog is running:5
the dog is running:6
the dog is running:7
the dog is running:8
the dog is running:9
the dog is breaking:0
the dog is breaking:1
the dog is breaking:2
the dog is breaking:3
the dog is breaking:4
the dog is breaking:5
the dog is breaking:6
the dog is breaking:7
the dog is breaking:8
the dog is breaking:9
可以看到只有当t1执行完runing方法后t2才能执行。
对象锁和全局锁
对象锁:
将锁添加在某一个对象身上,只需要synchronized
全局锁:
只针对当前类,无论这个类产生多少个对象,这些对象都共享这个锁,static synchronized
其实这两个锁结结合static关键字很容易理解。