内置锁:
每个java对象都可以用做一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
互斥锁:
内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
1.synchronized修饰普通方法:
锁住对象的实例,并不会锁住整个类的实例。
package com.xdclass.safe;
/**
* @author sqz
* @Description: 深入理解synchronized关键字
* @date 2019/8/6 13:39
*/
public class SynDemo {
//锁住对象的实例,并不会锁住整个类的实例
public synchronized void out() throws InterruptedException {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000L);
}
public static void main(String[] args) {
SynDemo synDemo = new SynDemo();
SynDemo synDemo2 = new SynDemo();
new Thread(()->{
try {
synDemo.out();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
synDemo2.out();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
测试结果:由此可见,synchronized修饰普通方法并不会锁住整个类,只会锁住单个实例,这里new了两个实例,两个线程访问普通out()方法,分别锁住各自实例的方法,并不会互相干扰
2.synchronized修饰静态方法:
锁住整个类
package com.xdclass.safe;
/**
* @author sqz
* @Description: 深入理解synchronized关键字
* @date 2019/8/6 13:39
*/
public class SynDemo {
//锁住对象的实例,并不会锁住整个类的实例
// public synchronized void out() throws InterruptedException {
// System.out.println(Thread.currentThread().getName());
// Thread.sleep(5000L);
// }
//锁住对象的实例,并不会锁住整个类的实例
public static synchronized void outStatic() throws InterruptedException {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000L);
}
public static void main(String[] args) {
new Thread(()->{
try {
SynDemo.outStatic();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
SynDemo.outStatic();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
如上实例,在打印了Thread-0之后隔了5秒才打印Thread-1,如果等待很长很长时间,那么后一个线程只能等待,也就是说,一个类如果有一个使用synchronized修饰了的静态方法,那么整个类都会被锁住,这整个类里面不管有多少个synchronized修饰的方法,锁已经被一个线程获取了,其他线程只能等待,所以一般在开发中最好不要用synchronized修饰静态方法,这是一个很危险的操作,他会导致其他线程一直在等待这个锁
3.修饰代码块
锁住一个对象 synchronized (lock) 即synchronized后面括号里的内容,这样并不会锁住一个方法或者整个类
package com.xdclass.safe;
/**
* @author sqz
* @Description: 深入理解synchronized关键字
* @date 2019/8/6 13:39
*/
public class SynDemo {
private Object lock = new Object();
//修饰代码块
public void myOut() {
synchronized (lock){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynDemo synDemo = new SynDemo();
new Thread(()->{
synDemo.myOut();
}).start();
new Thread(()->{
synDemo.myOut();
}).start();
}
}
如上图所示,在Thread-0打印5秒之后Thread-1才会打印,因为new了一个synDemo对象, 只有lock一个实例,两个线程start(),只有一个线程能够持有锁,等待一个线程执行完释放了锁另外一个才能获取到。如果new 两个synDemo实例,那么就会有两个lock对象,如果两个线程分别调用myOut()方法,那么会差不多时间打印,因为两个实例互不干涉。如下图所示:
package com.xdclass.safe;
/**
* @author sqz
* @Description: 深入理解synchronized关键字
* @date 2019/8/6 13:39
*/
public class SynDemo {
private Object lock = new Object();
//修饰代码块
public void myOut() {
synchronized (lock){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynDemo synDemo = new SynDemo();
SynDemo synDemo2 = new SynDemo();
new Thread(()->{
synDemo.myOut();
}).start();
new Thread(()->{
synDemo2.myOut();
}).start();
}
}