Condition的使用
「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
与synchronized再做一个比较:
Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法wait notify和notifyAll的使用;
使用Lock condition接口实现买票:
package com.JUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class shareDemo {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private int number = 0;
public void inc() throws InterruptedException {
lock.lock();
try{
while(number != 0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
/**
* 唤醒多有等待的线程
*/
condition.signalAll();
}finally {
lock.unlock();
}
}
public void sub(){
lock.lock();
try{
while(number != 1){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
/**
* 唤醒多有等待的线程
*/
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ConditionLocal {
public static void main(String[] args) {
shareDemo share = new shareDemo();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.inc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AAA").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
share.sub();
}
},"BBB").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.inc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CCC").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
share.sub();
}
},"DDD").start();
}
}
在书籍4.3.1-4.3.3对应的其实是该文章中线程通信的例子。
管道输入/输出流:
线程间通信的方式还有管道输入/输出流:与文件的输入输出不同的是,它主要用于线程间的数据传输,传输的媒介是内存;
以下是书中的内容:
管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、PipedInputStream、 PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。
实现例子:
对于Piped类型的流,必须先要进行绑定,也就是调用connect()方法,如果没有将输入/输
出流绑定起来,对于该流的访问将会抛出异常
package com.JUC;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
public class PipeInOut {
public static void main(String[] args) throws IOException {
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader();
//将输入流和输出流进行连接,否则会出现IO错误
out.connect(in);
//创建print线程来接收Main中的输入
Thread thread = new Thread(new Print(in),"PrintThread");
//开启该线程,开始接收数据
thread.start();
int receive = 0;
try {
//接收输入的数据并赋值
while((receive = System.in.read()) != -1){
out.write(receive);
}
}finally{
out.close();
}
}
static class Print implements Runnable {
private PipedReader in;
public Print(PipedReader in) {
this.in = in;
}
@Override
public void run() {
int receive = 0;
try {
while((receive = in.read()) != -1){
System.out.print((char) receive);
}
}catch(IOException ex){
}
}
}
}
Thread.join()的使用
书中的定义:其含义是:当前线程A等待thread线程终止之后才从thread.join()返回;感觉不太好理解;
Java 7 Concurrency Cookbook
是主线程等待子线程的终止。也就是说主线程的代码块中,如果碰到了t.join()方法,此时主线程需要等待(阻塞),等待子线程结束了(Waits for this thread to die.),才能继续执行t.join()之后的代码块。
例子:
package com.JUC;
import java.util.concurrent.TimeUnit;
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread previous = Thread.currentThread();
for (int i = 0; i < 10; i++) {
// 每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能从等待中返回
Thread thread = new Thread(new Domino(previous), String.valueOf(i));
thread.start();
previous = thread;
}
TimeUnit.SECONDS.sleep(5);
//主线程
System.out.println(Thread.currentThread().getName()+"--terminate.");
}
static class Domino implements Runnable {
private Thread thread;
public Domino(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//子线程
System.out.println(Thread.currentThread().getName()+"---Terminate.");
}
}
}
每个线程终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从join()方法返回;
我们查看下join方法的源码可以发现其中也是用的synchronized修饰的;
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
关于书中的4.3.6ThreadLocal的使用可以看下以前写的文章:点击进入
参考:
《JUC并发编程的艺术》
《【尚硅谷】大厂必备技术之JUC并发编程》