blog
blog copied to clipboard
Java 的 synchronized 关键字
trafficstars
synchronized 是 Java 用来同步的关键字,它修饰的类型如下:
- 修饰代码块:多线程访问同对象的同步代码块时阻塞
- 修饰方法:多线程访问同对象的同步方法时阻塞
- 修饰静态方法:多线程访问同步静态方法时阻塞
- 修饰类:多线程访问同步类的所有实例时,会出现阻塞
- 修饰 final 对象:关于此处为什么要是 final 在下文讲述
目前其性能虽然得到了提升,但是仍然不建议使用
synchroized 使用
修饰代码块
public class Test {
static class InnerClass {
public void execute() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " get lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class MyRunnable implements Runnable {
InnerClass innerClass;
MyRunnable(InnerClass innerClass) {
this.innerClass = innerClass;
}
@Override
public void run() {
innerClass.execute();
}
}
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
Thread t1 = new Thread(new MyRunnable(innerClass));
Thread t2 = new Thread(new MyRunnable(innerClass));
t1.start();
t2.start();
}
}
输出:
Thread-0 get lock
// 等待了 2s
Thread-1 get lock
修饰方法
public class Test {
static class InnerClass {
public synchronized void execute() {
System.out.println(Thread.currentThread().getName() + " get lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyRunnable implements Runnable {
InnerClass innerClass;
MyRunnable(InnerClass innerClass) {
this.innerClass = innerClass;
}
@Override
public void run() {
innerClass.execute();
}
}
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
Thread t1 = new Thread(new MyRunnable(innerClass));
Thread t2 = new Thread(new MyRunnable(innerClass));
t1.start();
t2.start();
}
}
输出:
Thread-0 get lock
// 等待了 2s
Thread-1 get lock
如果上述两个线程使用的 InnerClass 对象不同,则不会等待 2s,两个线程也不会出现阻塞同步的情况。
修饰静态方法
public class Test {
static class InnerClass {
public synchronized static void execute() {
System.out.println(Thread.currentThread().getName() + " get lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
InnerClass.execute();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
t1.start();
t2.start();
}
}
输出:
Thread-0 get lock
// 等待了 2s
Thread-1 get lock
修饰静态方法时候,拿到的是基于类的锁
修饰类
public class Test {
static class MyRunnable implements Runnable {
@Override
public void run() {
synchronized (MyRunnable.class) {
System.out.println(Thread.currentThread().getName() + " get lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
t1.start();
t2.start();
}
}
输出:
Thread-0 get lock
// 等待了 2s
Thread-1 get lock
由于我们把同步关键字加到了 MyRunnable 类上,同一时刻只能由一个 Thread 来获得同步锁,另一个线程发生阻塞
修饰 final 对象
public class Test {
static final InnerClass innerClass = new InnerClass();
static class InnerClass {
public void execute() {
synchronized (innerClass) {
System.out.println(Thread.currentThread().getName() + " get lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class MyRunnable implements Runnable {
InnerClass innerClass;
MyRunnable(InnerClass innerClass) {
this.innerClass = innerClass;
}
@Override
public void run() {
innerClass.execute();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable(innerClass));
Thread t2 = new Thread(new MyRunnable(innerClass));
Thread t3 = new Thread(new MyRunnable(innerClass));
t1.start();
t2.start();
t3.start();
}
}
输出:
Thread-0 get lock
// 等待 2s
Thread-2 get lock
// 等待 2s
Thread-1 get lock
为什么只能是修饰 final 对象?
这句话说得很好:
if the object reference changes, the same section of code may be run in parallel
如果对象引用改变,相同的代码可能会出现并行执行的问题
我们的 synchronized 是对对象的引用加锁,如果对象引用改变了,此处的同步就没什么意义了。
如下:
ublic class Test2 {
static InnerClass innerClass = new InnerClass();
static class InnerClass {
public void execute() {
synchronized (innerClass) {
System.out.println(Thread.currentThread().getName() + " get lock");
innerClass = new InnerClass();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class MyRunnable implements Runnable {
InnerClass innerClass;
MyRunnable(InnerClass innerClass) {
this.innerClass = innerClass;
}
@Override
public void run() {
innerClass.execute();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable(innerClass));
Thread t2 = new Thread(new MyRunnable(innerClass));
Thread t3 = new Thread(new MyRunnable(innerClass));
Thread t4 = new Thread(new MyRunnable(innerClass));
Thread t5 = new Thread(new MyRunnable(innerClass));
Thread t6 = new Thread(new MyRunnable(innerClass));
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
上述的几个线程会出现并行执行的情况。
wait 与 notify
public class Test {
static final Message message = new Message();
static class Message {
String msg;
void setMsg(String msg) {
System.out.println(msg);
this.msg = msg;
}
}
static class PassengerRunnable implements Runnable {
@Override
public void run() {
synchronized (message) {
try {
message.setMsg("乘客:司机,我位置准确,请来接我");
message.notify();
// 等待司机赶到定位地址
message.wait();
// 乘客赶往定位地址
Thread.sleep(2000);
System.out.println("乘客:好了,我也到了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class DriverRunnable implements Runnable {
@Override
public void run() {
synchronized (message) {
try {
message.wait();
// 司机赶往目标地点
Thread.sleep(2000);
message.setMsg("司机:我已到达定位地址,速来");
message.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
DriverRunnable driver = new DriverRunnable();
PassengerRunnable passenger = new PassengerRunnable();
new Thread(driver).start();
Thread.sleep(1000);
new Thread(passenger).start();
}
}
上边是 wait 和 notify 的简单使用,场景并不太合适。