一、使用volatile关键字
基于volatile关键字来实现线程间互相通信是使用贡献内存的思想,多个线程同时监听一个变量,当发生变化的时候,线程能够感知并执行相应业务,是最简单的一种实现方式。
public class TestSync {
private static volatile Integer num=0;
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
Thread t1=new Thread(()->{
for (int i=0;i<10;i++){
list.add(i);
num=list.size();
System.out.println("线程A添加元素,list的大小:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2=new Thread(()->{
while (true){
if(num==5){
System.out.println("线程B执行程序");
break;
}
}
});
//线程B先执行
t2.start();
t1.start();
}
}
二、使用Object类的wait()/notify()
Object类提供了线程间通讯方法Lwait()、notify()、notifyAll(),它们是多线程的基础。
wait/notify必须配合synchronized使用,wait方法释放锁,notify方法不释放锁。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他等待此锁的线程可以得到同步锁并运行煤制油其他线程调用了notify(),notify并不释放锁,只是告诉调用过wait的线程可以去获得锁的竞争,但不是马上获得锁,因为锁还在别人手中没有释放,调用wait()的一个或多个线程就会接触wait状态,重新竞争对象锁。
public class TestSync {
public static void main(String[] args) {
Object lock=new Object();
List<Integer> list=new ArrayList<>();
Thread t1=new Thread(()->{
synchronized (lock){
for (int i=0;i<10;i++){
list.add(i);
if(list.size()==5){
//唤醒线程b
lock.notify();
}
System.out.println("线程A添加元素,list的大小:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread t2=new Thread(()->{
while (true){
synchronized (lock){
if(list.size()!=5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B执行程序");
}
}
});
//线程B先执行
t2.start();
t1.start();
}
}
线程A发出notify()唤醒通知后依然走完自己线程的业务后,线程B才开始执行,说明notify()不释放锁,而wait()释放锁
三、使用JUC工具类
jdk1.5后再java.util.concurrent包下提供了并发线程相关工具类,简化了并发编程代码的编写,countDownLatch基于AQS框架,相当于也是维护了一个线程间共享的变量。
public class TestSync {
public static void main(String[] args) {
CountDownLatch countDownLatch=new CountDownLatch(1);
List<Integer> list=new ArrayList<>();
Thread t1=new Thread(()->{
for (int i=0;i<10;i++){
list.add(i);
System.out.println("线程A添加元素,list的大小:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(list.size()==5){
//唤醒线程b
countDownLatch.countDown();
}
}
});
Thread t2=new Thread(()->{
while (true){
if(list.size()!=5){
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B执行程序");
break;
}
});
//线程B先执行
t2.start();
t1.start();
}
}
四、使用ReentrantLock结合Condition
public class TestSync {
public static void main(String[] args) {
ReentrantLock lock=new ReentrantLock();
Condition condition=lock.newCondition();
List<Integer> list=new ArrayList<>();
Thread t1=new Thread(()->{
lock.lock();
for (int i=0;i<10;i++){
list.add(i);
System.out.println("线程A添加元素,list的大小:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(list.size()==5){
//唤醒线程b
condition.signal();
}
}
lock.unlock();
});
Thread t2=new Thread(()->{
lock.lock();
if(list.size()!=5){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B执行程序");
lock.unlock();
});
//线程B先执行
t2.start();
t1.start();
}
}
和Object的wait()/notify()方法一样,A线程signal()唤醒操作后不释放锁。
五、基于LockSupport实现线程间阻塞和唤醒
LockSupport是一种灵活线程间阻塞和唤醒工具,使用它不用关注是等待线程先进行还是唤醒线程先进行,但是得知道线程名称。
public class TestSync {
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
Thread t2=new Thread(()->{
if(list.size()!=5){
LockSupport.park();
}
System.out.println("线程B执行程序");
});
Thread t1=new Thread(()->{
for (int i=0;i<10;i++){
list.add(i);
System.out.println("线程A添加元素,list的大小:"+list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(list.size()==5){
//唤醒线程b
LockSupport.unpark(t2);
}
}
});
t1.start();
t2.start();
}
}