不同的调用方式
大多数的网页,都是用户点击某个链接,然后通过浏览器向后台发送请求,最后服务器响应请求,输出数据,并将数据展示到页面上,例如,搜索后点击某个结果链接。
整个过程可以用下面这张图表示。
这种基于请求-响应
的线性过程就是典型的同步调用
——一件事接着一件事发生,直到所有事件全部按顺序完成。
但还有一类应用和这种同步
过程有些不同。例如,在网页上聊天。
大多数的网页,都是用户点击某个链接,然后通过浏览器向后台发送请求,最后服务器响应请求,输出数据,并将数据展示到页面上,例如,搜索后点击某个结果链接。
整个过程可以用下面这张图表示。
这种基于请求-响应
的线性过程就是典型的同步调用
——一件事接着一件事发生,直到所有事件全部按顺序完成。
但还有一类应用和这种同步
过程有些不同。例如,在网页上聊天。
发布-订阅
模型,或者说生产者-消费者
模型,本质上就是对观察者
模式的应用。
它可以将提供服务的一方和消费服务的一方完全分离开,实现高度解耦,让它们彼此都可以完全按照自己的节奏来行动,互相不干扰,唯一的要求就是引入中间队列。
而且有了中间队列之后,对于生产者
(也就是发布者
)和消费者
(也就是订阅者
)来说,没有数量上的限制,也没有一一对应的要求。
在讲到并行时,有些资料中会提到关于泡茶的例子——也就是有两种泡茶方式。
可以看到,这两种方式消耗的时间是不同的。
完全可以在烧开水的同时去做其他的准备工作,例如,洗茶杯、准备茶叶等,把烧开水和其他准备事项并行起来做,可以大大节约总的耗时时间。
先用代码来展示第一种串行的泡茶方式
。
/**
* 烧开水
*
*/
public class BoilWater {
private boolean status = false;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
/**
* 其他准备工作
*
*/
public class OtherPreparation {
private boolean status = false;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
/**
* 串行的泡茶步骤
*
*/
public class SerialMakeTea {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread t1 = new Thread(() -> {
System.out.println("烧水开始:需要5分钟");
try {
BoilWater bw = new BoilWater();
bw.setStatus(true);
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("烧水结束");
});
t1.start();
t1.join();
Thread t2 = new Thread(() -> {
System.out.println("其他准备开始:需要2分钟");
try {
OtherPreparation tc = new OtherPreparation();
tc.setStatus(true);
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("其他准备结束");
});
t2.start();
t2.join();
System.out.println("总共耗时:" + (System.currentTimeMillis() - start) / 1000 + " 分钟");
}
}
在《Java深度探索:开发基础》的《第3章 多线程》
中讲过,多线程的stop()
已经被废弃,如果想要中断线程的执行,那么需要执行interrupt()
方法然后捕获InterruptedException
异常来实现。
但interrupt()
方法的本质其实是将处于WAITING
状态的线程唤醒,然后利用异常中断正在执行的线程,这算是取了个巧,用旁门左道
的方式来终止它。
第三方支付已经彻底融入了人们的生产和生活中了,它一定会出现下面这样的场景。
从上面的图中可以看到,当两个及两个以上账户之间同时转账时,就会出现余额
被锁住而互相等待的情况,这和多线程中的死锁
是一个道理。
死锁
的出现,一定是因为满足了下面的四个条件之一或全部。
互斥
:某个资源R只能被一个线程占用。
占有且等待
:线程A已经获取了资源R,并且想继续获取资源S,但它又不愿意释放R。
不可抢占
:其他的线程不能强行去抢占线程A已经获得的资源R,只能等待。
循环等待
:线程A等待线程B释放资源S,而线程B也等着线程A释放资源R。