课前作业:
1. 问题一:
当前有一个类 A
public class A {
public synchronized void a() {
}
public synchronized void b() {
}
}
然后创建两个对象:
A a1 = new A();
A a2 = new A();
然后在两个线程中并发访问如下代码:
Thread1 中执行 a1.a();
Thread2 中执行 a2.a();
请问二者能否构成线程同步?
2. 如果类 A 变成下面那样呢?
public class A {
public static synchronized void a() {
}
public static synchronized void b() {
}
}
正式开始:
理解 Java 中的 Synchronized 关键字
1.方法内的变量是线程安全的
方法内部的私有变量不存在非线程安全问题
2.实例变量是非线程安全的
多个线程访问同一个对象中的实例变量,则有可能出现非线程安全的问题
GuessIsAdult.java:根据年龄判断是否成年
/**
* 根据年龄是否大于 18 岁判断是否成年
*/
public class GuessIsAdult {
/**
* 是否成年 成年了:age >= 18 未成年:age < 18
*/
private boolean isAdult;
public void isAdult(int age) {
try {
if (age >= 18) {
isAdult = true;
Thread.sleep(2000);
} else {
isAdult = false;
}
if (isAdult) {
System.out.println("年龄" + age + ",我成年啦");
} else {
System.out.println("年龄" + age + ",我未成年");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class ThreadGuessA extends Thread {
private GuessIsAdult guessIsAdult;
public ThreadGuessA(GuessIsAdult guessIsAdult) {
this.guessIsAdult = guessIsAdult;
}
@Override
public void run() {
guessIsAdult.isAdult(19);
}
}
static class ThreadGuessB extends Thread {
private GuessIsAdult guessIsAdult;
public ThreadGuessB(GuessIsAdult guessIsAdult) {
this.guessIsAdult = guessIsAdult;
}
@Override
public void run() {
guessIsAdult.isAdult(17);
}
}
}
测试类 GuessMain.java
public class GuessMain {
public static void main(String[] args) {
GuessIsAdult guessIsAdult = new GuessIsAdult();
ThreadGuessA aThread = new ThreadGuessA(guessIsAdult);
aThread.start();
ThreadGuessB bThread = new ThreadGuessB(guessIsAdult);
bThread.start();
}
}
测试结果:
问题原因:
在 GuessIsAdult.isAdult() 方法中,当一个人年龄 19,则在线程睡眠 2s 之前,isAdult 还是个 true,2s 过后 isAdult 则早被年龄 17 那个人改成 false 了,显然这不安全的
解决办法:
只需要在 GuessIsAdult.isAdult() 方法最前面添加 synchronized 关键字即可
结论:
1.当 A 线程调用某个对象的 synchronized 方法 X 时,A 线程获得了 X 方法的对象锁,其它线程必须等 A 线程执行完毕才可以调用 X 方法,由于是对象锁,即使其它线程调用 synchronized 的非 X 方法,也必须等 A 线程把 X 方法执行完毕,等到释放了对象锁之后才可以调用该对象中的任意的 synchronized 方法
2.当 A 线程调用某个对象的 synchronized 方法 X 时,B 线程可随意调用该对象的非 synchronized 方法
作业答案:
1.不能同步,new 了 2 个对象,就有2 个锁,则方法异步执行
2.能同步,synchronized 关键字加到静态方法前,获取的锁是类对应的锁,该锁对类的所有对象都有效,则方法同步执行