为了保证同一数据在不同线程中被访问的正确性,Java 提供了访问时的锁机制 synchronized,当一个线程获得对象的排他锁,该线程就可以独占该资源,其他线程必须等待,直至有用锁的线程释放锁。但是存在以下问题:
- 一个线程持有锁会导致其他所有需要锁的线程挂起
- 在多线程竞争下,加锁和释放锁会导致比较多的上下文切换和调度延时,降低程序性能
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引发性能问题
1 synchronized 关键字
private
关键字帮助我们保证了数据只能被对象的方法访问。synchronized
关键字是为了保护对象属性的。保护synchronized
关键字有两种用法:
- synchronized 方法
public synchronized void method(int args) {}
- synchronized 代码块
synchronized (Obj) { ... }
1.1 synchronized 方法
synchronized 方法 :public synchronized void method(int args) {}
- 该方法控制对“对象”的访问,每个对象对应一把锁
- 每个 synchronized 方法都必须获得调用该方法的对象的锁才能执行,否则就会阻塞
若将一个大方法声明为
synchronized
将会影响效率。所以明确代码临界区后,将临界区代码抽象出来作为**synchronized**
方法是较好的做法。public class TestSync implements Runnable {
private int numTickets = 10;
private boolean flag = true;
public static void main(String[] args) {
TestSync testSync = new TestSync();
new Thread(testSync, "lrt").start();
new Thread(testSync, "dhr").start();
new Thread(testSync, "yyds").start();
}
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if (numTickets <= 0) {
flag = false;
return;
}
Thread.sleep(100);
// Buy a ticket.
System.out.println(Thread.currentThread().getName() + " 拿到了第 " + numTickets-- + " 张票");
}
}
执行结果:
dhr 拿到了第 9 张票
yyds 拿到了第 8 张票
lrt 拿到了第 10 张票
lrt 拿到了第 6 张票 // 重复了
yyds 拿到了第 7 张票
dhr 拿到了第 6 张票 // 重复了
yyds 拿到了第 5 张票
dhr 拿到了第 3 张票
lrt 拿到了第 4 张票
yyds 拿到了第 2 张票 // 重复了
lrt 拿到了第 2 张票 // 重复了
dhr 拿到了第 1 张票
可以发现出现了线程不安全的情况。
使用synchronized
关键字,只需要将临界区方法用synchronized
关键字修饰就可以,即将上面的buy()
方法改成private synchronized void buy() ...
即可。
使用 synchronized 方法的执行结果:
lrt 拿到了第 10 张票
lrt 拿到了第 9 张票
lrt 拿到了第 8 张票
lrt 拿到了第 7 张票
lrt 拿到了第 6 张票
lrt 拿到了第 5 张票
lrt 拿到了第 4 张票
lrt 拿到了第 3 张票
yyds 拿到了第 2 张票
yyds 拿到了第 1 张票
这里发现了一个问题,线程之间基本不会交替运行,而我最开始的几次测试时只有“lrt”线程执行了,其他的线程一直没有得到执行。这其实跟 CPU 的性能有关,如果 CPU 性能比较好,则需要将numTickets
变量值增加才能看到线程的交替。
1.2 synchronized 代码块
synchronized 代码块:synchronized (Obj) { ... }
Obj
被称为同步监视器
- 可以是任何对象,但推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为其监视器就是
this
,或者是class
(static 方法)