1.synchronized–wait–notify
package com.fp.coupon.rest.facade;
public class Test {
static Object object = new Object();
public static void main (String[] args) {
new Thread(() -> {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "coming in");
try {
object.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "通知");
object.notify();
}
}, "B").start();
}
}
1.1不加锁执行
public class Test {
static Object object = new Object();
public static void main (String[] args) {
new Thread(() -> {
// synchronized (object){
System.out.println(Thread.currentThread().getName() + "coming in");
try {
object.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
}
// }
}, "A").start();
new Thread(() -> {
// synchronized (object){
System.out.println(Thread.currentThread().getName() + "通知");
object.notify();
// }
}, "B").start();
}
}
1.2修改执行顺序
public class Test {
static Object object = new Object();
public static void main (String[] args) {
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
}
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "coming in");
try {
object.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "通知");
object.notify();
}
}, "B").start();
}
}
- 线程B休息3秒,线程A正常被唤醒
- 线程A休息3秒,线程B coming in方法正常执行,但是不会被唤醒!
2.lock–await–signal
public class Test {
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main (String[] args) {
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "coming in");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "A").start();
new Thread(() -> {
try {
lock.lock();
condition.signal();
System.out.println(Thread.currentThread().getName() + "通知");
} finally {
lock.unlock();
}
}, "B").start();
}
}
2.1不加锁执行
package com.fp.coupon.rest.facade;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main (String[] args) {
new Thread(() -> {
try {
// lock.lock();
System.out.println(Thread.currentThread().getName() + "coming in");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// lock.unlock();
}
}, "A").start();
new Thread(() -> {
try {
// lock.lock();
condition.signal();
System.out.println(Thread.currentThread().getName() + "通知");
} finally {
// lock.unlock();
}
}, "B").start();
}
}
2.2修改执行顺序
public class Test {
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main (String[] args) {
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
lock.lock();
System.out.println(Thread.currentThread().getName() + "coming in");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "A").start();
new Thread(() -> {
try {
// TimeUnit.SECONDS.sleep(3);
lock.lock();
condition.signal();
System.out.println(Thread.currentThread().getName() + "通知");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "B").start();
}
}
- 线程B休息3秒,线程A正常被唤醒
- 线程A休息3秒,线程B coming in方法正常执行,但是不会被唤醒!
3.LockSupport
public class Test {
public static void main (String[] args) {
Thread a = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "coming in");
LockSupport.park();//被阻塞,等待唤醒
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
}
}, "A");
a.start();
Thread b = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "通知");
LockSupport.unpark(a);//唤醒线程a
} catch (Exception e) {
e.printStackTrace();
}
}, "B");
b.start();
}
}
3.1修改执行顺序
public class Test {
public static void main (String[] args) {
Thread a = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "coming in");
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (Exception e) {
e.printStackTrace();
}
}, "A");
a.start();
Thread b = new Thread(() -> {
try {
// TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "通知");
LockSupport.unpark(a);
} catch (Exception e) {
e.printStackTrace();
}
}, "B");
b.start();
}
}
- 线程B休息3秒,线程A正常被唤醒
- 线程A休息3秒,线程B coming in方法正常执行,可以被唤醒!
3.2是什么
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport是一个线程阻塞工具类,所有的方法都是静态的,可以让线程在任意位置阻塞,阻塞后也有相应的唤醒方法。
3.3使用
提供park()和unpark () 方法实现阻塞线程和接触线程阻塞。
每个线程都有一个许可(permit)关联,permit相当于1,0的开关,默认是0;
调用一次unpark就➕1变成1;
调用一次park就会消费permit,也就是变成0,同时park立即返回。
如果再次调用park会变成阻塞(因为permit=0的时候会阻塞,一直等到permit变成1),这时候需要调用unpark会把permit置为1
3.4面试题
为什么可以先唤醒线程后阻塞线程?
因为unpark获得了一个凭证,只有在调用park方法,就可以名正言顺的消费凭证,因此不会被阻塞。
为什么唤醒两次后阻塞两次,最终结果还是阻塞线程?
因为凭证的数量最多为1,两次unpark和一次unpark的结果是一样的,只会增加一个凭证;
调用两次park却需要消费两个凭证,证不够,不能放行