1、前言
继《【JUC基础】03.几段代码看懂synchronized(上)》,我们继续接下来几段代码块。
程序员经常听到“并发锁”这个名词,而且实际项目中也确实避免不了要加锁。那么什么是锁?锁的是什么?今天文章从8个有意思的案例,彻底弄清这两个问题。
2、代码五、单实例static和synchronized调用
eat():静态synchronized方法
sleep():静态synchronized方法
这里eat()和sleep()添加static修饰。
思考问题:控制台先打印什么?先吃饭?还是先睡觉?
public class LockDemo {
public static void main(String[] args) throws InterruptedException {
Dog dog = new Dog();
new Thread(() -> dog.eat()).start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> dog.sleep()).start();
}
}
class Dog {
public static synchronized void eat(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("先吃饭");
}
public static synchronized void sleep(){
System.out.println("先睡觉");
}
}
执行结果:
原因:
这里加了static之后,synchronized锁的就不再是调用者实例了。由于static特性,这里锁定的是整个class类,而不是实例。
3、代码六、多实例static和synchronized调用
eat():静态synchronized方法
sleep():静态synchronized方法
这里eat()和sleep()添加static修饰。
思考问题:控制台先打印什么?先吃饭?还是先睡觉?
public class LockDemo {
public static void main(String[] args) throws InterruptedException {
Dog dog1 = new Dog();
Dog dog2 = new Dog();
new Thread(() -> dog1.eat()).start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> dog2.sleep()).start();
}
}
class Dog {
public static synchronized void eat(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("先吃饭");
}
public static synchronized void sleep(){
System.out.println("先睡觉");
}
}
执行结果:
原因:
这里加了static之后,synchronized锁的就不再是调用者实例了。由于static特性,这里锁定的是整个class类,而不是实例。因此这里跟多实例调用无关。
4、代码七、单实例static同步方法和普通方法调用
eat():静态synchronized方法
sleep():普通synchronized方法
思考问题:控制台先打印什么?先吃饭?还是先睡觉?
public class LockDemo {
public static void main(String[] args) throws InterruptedException {
Dog dog = new Dog();
new Thread(() -> dog.eat()).start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> dog.sleep()).start();
}
}
class Dog {
public static synchronized void eat(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("先吃饭");
}
public synchronized void sleep(){
System.out.println("先睡觉");
}
}
执行结果:
原因:
eat()这里加了static之后,synchronized锁的是整个class类,而sleep()锁的是调用者实例dog;因此锁互不影响。
5、代码八、多实例static同步方法和普通方法调用
eat():静态synchronized方法
sleep():普通synchronized方法
思考问题:控制台先打印什么?先吃饭?还是先睡觉?
public class LockDemo {
public static void main(String[] args) throws InterruptedException {
Dog dog1 = new Dog();
Dog dog2 = new Dog();
new Thread(() -> dog1.eat()).start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> dog2.sleep()).start();
}
}
class Dog {
public static synchronized void eat(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("先吃饭");
}
public synchronized void sleep(){
System.out.println("先睡觉");
}
}
执行结果:
原因:
eat()这里加了static之后,synchronized锁的是整个class类,而sleep()锁的是调用者实例dog;因此锁互不影响。
6、小结
因此,对于锁。我们需要搞懂锁是谁持有的,通常一般属于调用者持有。调用者又分两类:实例调用(new),类调用(static)。这里用最简单的代码来理解锁。当然锁的机制远比这里的复杂很多,这里只是入门。