java线程间通信
java线程间通信模型可分为两种:共享内存和消息传递。该文章主要对共享内存的方式介绍线程间的通信
一、volatile 关键字
使用volatile关键字修饰变量,将会使变量线程间可见即每次获取都是最新的值。如下文的示例代码,通过volatile修饰标志变量flag,使得线程在获取flag的值时总是最新的,每个线程对flag进行更改,以此达到线程间的通信
public class VolatileDemo {
volatile Boolean flag = false;
public static void main(String[] args) {
VolatileDemo volatileDemo = new VolatileDemo();
// 生产者任务
Thread producerThread = new Thread(volatileDemo.producer());
producerThread.start();
// 消费者
Thread consumerThread = new Thread(volatileDemo.consumer());
consumerThread.start();
}
public Runnable producer() {
return new Runnable() {
@Override
public void run() {
while (true) {
// 当flag为false时,生产
while (!flag) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("Produce complete: " + System.currentTimeMillis());
}
}
}
};
}
public Runnable consumer() {
return new Runnable() {
@Override
public void run() {
while (true) {
// 当flag为true时,消费
while (flag) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = false;
System.out.println("Consume complete: " + System.currentTimeMillis());
}
}
}
};
}
}
// 执行结果
Produce complete: 1597651318001
Consume complete: 1597651320001
Produce complete: 1597651322002
Consume complete: 1597651324002
Produce complete: 1597651326002
Consume complete: 1597651328003
...
// 当去掉volatile关键字后的执行结果
Produce complete: 1597651376746
上述代码的执行结果中,当volatile关键字去掉后,由于生产者线程修改的flag并没有即时写入主内存,导致消费者线程读取的变量值总是为false。
二、synchronized关键字+wait()、notify()
通过synchronized关键字修饰方法、代码块时实现线程同步时需要获取到锁,其锁对象也需要是同一个,因此实际上也是共享内存变量的模型。
public class SynchronizedDemo {
static final Object lockObj = new Object();
public static void main(String[] args) throws InterruptedException {
// 生产者
Thread producer = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockObj){
while (true){
try {
Thread.sleep(2000);
System.out.println("Produce complete : "+ System.currentTimeMillis());
lockObj.notifyAll();
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
producer.start();
// 消费者
Thread consumer = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockObj){
while (true){
try {
Thread.sleep(2000);
System.out.println("Consume complete : "+ System.currentTimeMillis());
lockObj.notifyAll();
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
consumer.start();
}
}
//执行结果
Produce complete : 1597653867586
Consume complete : 1597653869587
Produce complete : 1597653871587
Consume complete : 1597653873587
Produce complete : 1597653875588
Consume complete : 1597653877589
...
上面的代码中,通过共享lockObj这个对象的,两个进程各自通过lockObj的wati()释放锁并等待,通过notifyAll()方法相互唤醒,实现了两个线程间的通信。
三、ReentrantLock+Condition
该方法与synchronized类似,也是共享一个对象,不过这里共享的是ReentrantLock锁对象。ReentrantLock对象的newCondition()方法可以返回其Condition对象,该Condition对象有独有的await()和signal()、signalAll()方法,类似于Object对象的wait()和notify()方法,但是拥有更灵活的扩展用法。
public class ReentrantLockDemo {
final static ReentrantLock lock = new ReentrantLock();
final static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
// 生产者
Thread producer = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
lock.lock();
Thread.sleep(2000);
System.out.println("Produce complete : " + System.currentTimeMillis());
condition.signalAll();
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
});
producer.start();
Thread.sleep(500);
// 消费者
Thread consumer = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
lock.lock();
Thread.sleep(2000);
System.out.println("Consume complete : " + System.currentTimeMillis());
condition.signalAll();
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
});
consumer.start();
}
}
// 执行结果
Produce complete : 1597654555715
Consume complete : 1597654557715
Produce complete : 1597654559716
Consume complete : 1597654561716
...
四、CyclicBarrier
CyclicBarrier可以理解为一个栅栏,等所有的线程都执行到指定的进度——栅栏之后,每个线程才会继续运行栅栏之后的任务。
public class CyclicBarrierDemo {
final static CyclicBarrier barrier = new CyclicBarrier(3);
public static void main(String[] args) {
// 线程1
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread()+"Stopped at barrier");
barrier.await();
System.out.println(Thread.currentThread()+"More task");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
// 线程2
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread()+"Stopped at barrier");
try {
barrier.await();
System.out.println(Thread.currentThread()+"More task");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
// 线程3
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread()+"Stopped at barrier");
try {
barrier.await();
System.out.println(Thread.currentThread()+"More task");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
}
}
// 执行结果
Thread[Thread-1,5,main]Stopped at barrier
Thread[Thread-2,5,main]Stopped at barrier
Thread[Thread-0,5,main]Stopped at barrier
Thread[Thread-0,5,main]More task
Thread[Thread-1,5,main]More task
Thread[Thread-2,5,main]More task
在线程1中,sleep了1秒,使得线程2和3先到达栅栏,等线程1也到达栅栏后,每个线程后续的任务才继续执行