Condition
Condition 不像 wait()
、notify()
和 notifyAll()
这些方法一样,直接使用就行,而是先需要生成一个 Condition 对象,比较通用的就是从 ReentrantLock 的 newCondition()
方法中获取一个 Condition 对象。
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Condition 无法单独使用,还是需要先获取到锁之后再使用。
示例:用condition精准唤醒
public class Test05 { public static void main(String[] args) { Data3 data = new Data3(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.change1(); } catch (InterruptedException e) { throw new RuntimeException(e); } } },"A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.change2(); } catch (InterruptedException e) { throw new RuntimeException(e); } } },"B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.change3(); } catch (InterruptedException e) { throw new RuntimeException(e); } } },"C").start(); } } class Data3 { private int num = 1; Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Condition condition3 = lock.newCondition(); void change1() throws InterruptedException { try { lock.lock(); while (num != 1){ condition1.await(); } System.out.println(Thread.currentThread().getName() + ": " + num); num = 2; condition2.signal(); } catch (Exception e) { throw new RuntimeException(e); } finally { lock.unlock(); } } void change2() throws InterruptedException { try { lock.lock(); while (num != 2){ condition2.await(); } System.out.println(Thread.currentThread().getName() + ": " + num); num = 3; condition3.signal(); } catch (Exception e) { throw new RuntimeException(e); } finally { lock.unlock(); } } void change3() throws InterruptedException { try { lock.lock(); while (num != 3){ condition3.await(); } System.out.println(Thread.currentThread().getName() + ": " + num); num = 1; condition1.signal(); } catch (Exception e) { throw new RuntimeException(e); } finally { lock.unlock(); } } }
此时线程ABC按顺序进行工作,在方法中实现了对单个线程的精准唤醒。
理解八锁现象
1.
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendMessage();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
public synchronized void sendMessage() {
System.out.println("发信息");
}
public synchronized void call() {
System.out.println("打电话");
}
}
多次运行进行测试,发现都是先打印发短信,再打印打电话
2.
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendMessage();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
public synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发信息");
}
public synchronized void call() {
System.out.println("打电话");
}
}
此时仍然先打印发信息,再打印打电话。因为synchronized锁的对象是方法的调用者,在上述两方法sendMessage和call方法中使用的是同一个锁,先拿到锁的方法先执行。
3.增加一个普通方法,此时先打印发信息还是Hello?
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendMessage();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone.hello();
}, "B").start();
}
}
class Phone {
public synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发信息");
}
public synchronized void call() {
System.out.println("打电话");
}
public void hello() {
System.out.println("Hello");
}
}
此时先打印Hello,因为hello方法不是同步方法,不受synchronized锁的影响。
4.增加一个对象,两个线程分别调用两个对象的同步方法,此时先打印发信息还是打电话?
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendMessage();
}, "A").start();
// TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone {
public synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发信息");
}
public synchronized void call() {
System.out.println("打电话");
}
public void hello() {
System.out.println("Hello");
}
}
此时先打印打电话,后打印发信息,因为有两个不同的对象,所以有两个调用者,即两把锁,由于sendMessage方法中存在延时,所以先打印打电话。
5.增加两个静态方法,用同一个对象进行测试,此时是先打印发信息还是打电话?
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
// Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendMessage();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone1.call();
}, "B").start();
}
}
class Phone {
public static synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发信息");
}
public static synchronized void call() {
System.out.println("打电话");
}
public void hello() {
System.out.println("Hello");
}
}
此时先打印打电话,因为static是静态方法,在静态方法中synchronized锁锁的是Phone的Class模版,由于Phone的Class模版全局唯一,所以两线程共用一把锁,发信息线程先获得锁,所以先执行。
6.增加一个对象,此时两个对象分别调用两个静态同步方法,先执行发信息还是打电话?
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendMessage();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone {
public static synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发信息");
}
public static synchronized void call() {
System.out.println("打电话");
}
public void hello() {
System.out.println("Hello");
}
}
此时仍然是先打印发信息,理由与情况5相同,对象Phone1和Phone2的Class模版相同(Phone类的Class模版全局唯一),所以Phone1和Phone2共用一把锁,先获得锁的先执行。
7.一个静态同步方法,一个普通同步方法,用同一个对象调用,此时是先打印发信息还是打电话?
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
// Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendMessage();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone1.call();
}, "B").start();
}
}
class Phone {
public static synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发信息");
}
public synchronized void call() {
System.out.println("打电话");
}
public void hello() {
System.out.println("Hello");
}
}
此时先打印打电话,再打印发信息,因为sendMessage方法中的锁锁的是Class类模版,call方法中的锁锁的是调用者,因此是两把不同的锁。
8.一个静态同步方法,一个普通同步方法,用两个对象进行调用,先打印发信息还是打电话?
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone1.sendMessage();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone {
public static synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("发信息");
}
public synchronized void call() {
System.out.println("打电话");
}
public void hello() {
System.out.println("Hello");
}
}
此时也是先打印打电话,再打印发信息,原因与7中相同。