Java多线程实战--奇偶数打印

本文开始,将会以各种多线程相关的题目为基础,逐一进行分析,先开始从一个简单的例子开始。

问题剖析

题目:编号分别为 0 和 1 的线程,按照 ”偶数、奇数、偶数、奇数“ 的顺序输出结果,请严格确保输出顺序、线程能够正确退出。

方案如下,该方案有瑕疵,请谨慎阅读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Solution {
static class SolutionTask implements Runnable{
static int value = 0;
@Override
public void run() {
while (value <= 100){
synchronized (SolutionTask.class){
System.out.println(Thread.currentThread().getName() + ":" + value++);
SolutionTask.class.notify();
try {
SolutionTask.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
new Thread(new SolutionTask(), "偶数").start();
new Thread(new SolutionTask(), "奇数").start();
}
}

方案分析

该方案其实存在以下的一些问题

  • 线程最后不会自动退出。最后一个线程执行 SolutionTask.class.notify() 后再执行 SolutionTask.class.wait(),因而线程一直在等待。
  • 输出顺序不一定是 ”偶数、奇数“,有可能是 ”奇数、偶数“ 的情况,涉及到线程切换的问题。

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Solution {
static class SolutionTask implements Runnable{
static int value = 0;
int threadNo;

SolutionTask(int threadNo) {
this.threadNo = threadNo;
}

@Override
public void run() {
while (value <= 100) {
synchronized (SolutionTask.class){
// 判断线程对应关系,避免乱序
while (value % 2 != threadNo) {
// 确保最后一个线程被唤醒后能够退出
if (value > 100) {
break;
}

try {
SolutionTask.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

if (value > 100) {
break;
}

System.out.println(Thread.currentThread().getName() + ":" + value++);
SolutionTask.class.notify();
}
}
}
}
public static void main(String[] args) {
new Thread(new SolutionTask(0), "偶数").start();
new Thread(new SolutionTask(1), "奇数").start();
}
}

处理类似问题的时候,也一定要对边界情况进行思考,特别是开始、结束部分,避免出现不符合题意的输出结果,类似于数据中的归纳法证明。

还有另外一点需要注意,线程共享变量 value 没必要使用 volatile 变量,原因是当线程被唤醒后再次通过 sychronized 获取互斥锁,sychronized 已经可以保证变量的可见性了。

Hexo使用中遇到的问题以及解决办法

安装过程中的问题

Kong插件开发-重写响应消息体

本篇文章主要讲述如何通过 kong 插件重写响应给客户端的消息体内容,以下内容基于 kong v1.4.x 版本,官方文档

按照官方文档,可以通过以下指令来重写返回给客户端的消息体,但只有在特定阶段才能重写

1
2
3
4
kong.response.exit(status[, body[, headers]])

Phase:
rewrite, access, admin_api, header_filter (only if body is nil)

假设我们的需求是当收到 Upstream 返回失败时(Http Status Code >= 400 )修改返回客户端的响应体,使用 kong pdk 中的接口都会提示执行错误,因为 header_filter 以及以后的阶段是不能直接修改的。

但我们可以通过执行 ngx 的一些指令来实现这个操作,该用法出现在 kong 官方插件 response-transformer 中,经过测试,可以满足该需求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
local kong = kong
local ngx = ngx
local concat = table.concat

# 因为会修改返回的响应体内容,所以要去掉此 header
kong.response.clear_header("Content-Length")

# 修改返回给调用端的响应体内容
local ctx = ngx.ctx
local chunk, eof = ngx.arg[1], ngx.arg[2]

ctx.rt_body_chunks = ctx.rt_body_chunks or {}
ctx.rt_body_chunk_number = ctx.rt_body_chunk_number or 1

if eof then
local chunks = concat(ctx.rt_body_chunks)
local body = responseBody
ngx.arg[1] = body or chunks
else
ctx.rt_body_chunks[ctx.rt_body_chunk_number] = chunk
ctx.rt_body_chunk_number = ctx.rt_body_chunk_number + 1
ngx.arg[1] = nil
end
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×