Object类自带的方法
使用wait()方法来阻塞线程,使用notify()和notifyAll()方法来唤醒线程。
调用wait()方法后,线程将被阻塞,wait()方法将会释放当前持有的监视器锁(monitor),直到有线程调用notify/notifyAll()方法后方能继续执行。
notify/notifyAll()方法只是解除了等待线程的阻塞,并不会马上释放监视器锁,而是在相应的被synchronized关键字修饰的同步方法或同步代码块执行结束后才自动释放锁。
默认使用非公平锁,无法修改。
缺点:
- 使用几个方法时,必须处于被synchronized关键字修饰的同步方法或同步代码块中,否则程序运行时,会抛出IllegalMonitorStateException异常。
- 线程的唤醒必须在线程阻塞之后,否则,当前线程被阻塞之后,一直没有唤醒,线程将会一直等待下去(对比LockSupport)
public class SynchronizedDemo {
// 三个线程交替打印ABC
public static void main(String[] args) {
Print print = new Print();
new Thread(() -> {
while (true) {
print.printA();
}
}, "A").start();
new Thread(() -> {
while (true) {
print.printB();
}
}, "B").start();
new Thread(() -> {
while (true) {
print.printC();
}
}, "C").start();
}
}
class Print {
Object object = new Object();
int num = 1;
public void printA() {
synchronized (object) {
try {
while (num != 1) {
object.wait();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "==>A");
}
num = 2;
object.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void printB() {
synchronized (object) {
try {
while (num != 2) {
object.wait();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "==>B");
}
num = 3;
object.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void printC() {
synchronized (object) {
try {
while (num != 3) {
object.wait();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "==>C");
}
num = 1;
object.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Condition接口
使用await()方法来阻塞线程,signal()/singnalAll()方法来唤醒线程。
需要使用lock对象的newCondition()方法获得Condition条件对象(可有多个)。
可实现公平锁,默认是非公平锁
缺点:
- 必须被Lock包裹,否则会在运行时抛出IllegalMonitorStateException异常。
- 线程的唤醒必须在线程阻塞之后
- Lock的实现是基于AQS,效率稍高于synchronized
public class ConditionDemo {
// 三个线程交替打印ABC
public static void main(String[] args) {
Print print = new Print();
new Thread(() -> {
while (true) {
print.printA();
}
}, "A").start();
new Thread(() -> {
while (true) {
print.printB();
}
}, "B").start();
new Thread(() -> {
while (true) {
print.printC();
}
}, "C").start();
}
}
class Print {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num = 1;
public void printA() {
lock.lock();
try {
while (num != 1) {
condition1.await();
}
for (int i = 0; i < 5; ++i) {
System.out.println(Thread.currentThread().getName() + "==>A");
}
num = 2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (num != 2) {
condition2.await();
}
for (int i = 0; i < 10; ++i) {
System.out.println(Thread.currentThread().getName() + "==>B");
}
num = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (num != 3) {
condition3.await();
}
for (int i = 0; i < 15; ++i) {
System.out.println(Thread.currentThread().getName() + "==>C");
}
num = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
LockSupport
使用park()来阻塞线程,用unpark()方法来唤醒线程。
这里有一个许可证的概念,许可不能累积,并且最多只能有一个许可,只有1和0的区别。
特点:
- 使用灵活,可以直接使用
- 线程唤醒可在线程阻塞之前,因为调用unpark()方法后,线程已经获得了一个许可证(但也只能有一个许可证),之后阻塞时,可以直接使用这个许可证来通行。
- 效率高
public class LockSupportDemo {
// 三个线程交替打印ABC
public static void main(String[] args) throws Exception {
Print print = new Print();
Thread threadA = new Thread(() -> {
while (true) {
print.printA();
}
}, "A");
Thread threadB = new Thread(() -> {
while (true) {
print.printB();
}
}, "B");
Thread threadC = new Thread(() -> {
while (true) {
print.printC();
}
}, "C");
threadA.start();
threadB.start();
threadC.start();
while (true) {
LockSupport.unpark(threadA);
LockSupport.unpark(threadB);
LockSupport.unpark(threadC);
}
}
}
class Print {
private int num = 1;
public void printA() {
while (num != 1) {
LockSupport.park();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "==>A");
}
num = 2;
}
public void printB() {
while (num != 2) {
LockSupport.park();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "==>B");
}
num = 3;
}
public void printC() {
while (num != 3) {
LockSupport.park();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "==>C");
}
num = 1;
}
}