Fork me on GitHub

Wait/Notify通知机制解析

原文链接:Wait/Notify通知机制解析

Wait/Notify通知机制解析

前言

我们知道,java的wait/notify的通知机制可以用来实现线程间通信。wait表示线程的等待,调用该方法会导致线程阻塞,直至另一线程调用notify或notifyAll方法才可另其继续执行。经典的生产者、消费者模式即是使用wait/notify机制得以完成。在这篇文章中,我们将深入解析这一机制,了解其背后的原理。

线程的状态

在了解wait/notify机制前,先熟悉一下java线程的几个生命周期。分别为初始(NEW)、运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)、终止(TERMINATED)等状态(位于java.lang.Thread.State枚举类中)。

以下是对这几个状态的简要说明,详细说明见该类注释。

对于以上线程间的状态及转化关系,我们需要知道

  1. WAITING(等待状态)和TIMED_WAITING(超时等待)都会令线程进入等待状态,不同的是TIMED_WAITING会在超时后自行返回,而WAITING则需要等待至条件改变。
  2. 进入阻塞状态的唯一前提是在等待获取同步锁。java注释说的很明白,只有两种情况可以使线程进入阻塞状态:一是等待进入synchronized块或方法,另一个是在调用wait()方法后重新进入synchronized块或方法。下文会有详细解释。
  3. Lock类对于锁的实现不会令线程进入阻塞状态,Lock底层调用LockSupport.park()方法,使线程进入的是等待状态。
阅读更多...

java 多线程之使用 interrupt 停止线程的几种方法

原文地址: java 多线程之使用 interrupt 停止线程的几种方法

停止线程的方式总结

  1. 使用退出标志,使线程正常退出,也就是当 run 方法完成后才停止;
  2. 使用 stop 方法强行终止线程,是过期作废的方法,这种方法可以排除不用;
  3. 使用 interrupt 方法终止线程。
    • 异常法停止线程(推荐)
    • 在 sleep() 中停止线程
    • 使用 return 停止线程

停止线程

停止线程是 java 多线程开发中很重要的技术点,实际工作中很多业务都有类似的需求,掌握此技术可以对业务中所需停止的线程做有效的处理。但是停止线程在 java 语言中并不像 break 语句中那样干脆简单,还需要我们做一下技巧性的处理。 如何更好的停止一个线程是我们应该要考虑的问题,停止一个线程意味着在线程处理完任务之前停掉正在做的操作,即放弃当前的操作。停止一个线程在之前老的 JDK 中使用的是Thread.stop()方法,但是后面发现这种处理方法是很危险而且不安全的,由于 stop()方法已经在 JDK 中被标明是“作废/过期”的方法,显然它在功能上是具有缺陷的。作为一个负责的 java 工程师,最好是不要去使用它,因为使用 stop()方法释放锁将会给数据造成不一致性的结果。如果出现这种情况,程序处理的数据可能遭到破坏,最终导致程序执行流程错误。 大多数停止一个线程的操作使用的是Thread.interrupt()方法,虽然方法名是“终止,停止”的意思,但是这个方法不会直接终止一个正在运行的线程还需要加入一个判断才可以完成线程的停止。 此外,在 java 中有以下的 3 种方法可以终止正在运行的线程:

  1. 使用退出标志,使线程正常退出,也就是当 run 方法完成后才停止;
  2. 就是我们上面所说的使用 stop 方法强行终止线程,是过期作废的方法,这种方法可以排除不用;
  3. 使用 interrupt 方法终止线程。

第一种使用退出标志的方法楼主不做介绍,具体可以参考其他博客,都大同小异,楼主写这篇博客主要是探索使用 interrupt 停止线程的几种方法的优劣。

异常法停止线程

首先我们用 for-break 方式停止线程:

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
public class ExceptionInterrupt extends Thread {

@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
if (this.isInterrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("我被输出,如果此代码是for又继续运行,线程并未停止!");
}

public static void main(String[] args) {
ExceptionInterrupt thread = new ExceptionInterrupt();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();

} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end!");
}
}

打印结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
......
i=421200
i=421201
i=421202
i=421203
i=421204
i=421205
i=421206
i=421207
i=421208
i=421209
已经是停止状态了!我要退出了!
我被输出,如果此代码是for又继续运行,线程并未停止!
end!

我们发现,上面的示例虽然停止了线程,但是如果 for 语句下面还有语句,那么还是会继续执行。所以我们这里用异常法来停止线程,就不会继续执行 for 后面的业务逻辑了。

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
public class ExceptionInterrupt2 extends Thread {

@Override
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
if (this.isInterrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("我在for下面");
} catch (InterruptedException e) {
System.out.println("进入ExceptionInterrupt2 类中run方法的catch了!");
e.printStackTrace();
}
}

public static void main(String[] args) {
ExceptionInterrupt2 thread = new ExceptionInterrupt2();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();

} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}

打印结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
......
i=96487
i=96488
i=96489
i=96490
i=96491
i=96492
i=96493
已经是停止状态了!我要退出了!
进入ExceptionInterrupt2 类中run方法的catch了!
java.lang.InterruptedException
at com.thread.chapter1.ExceptionInterrupt2.run(ExceptionInterrupt2.java:20)
end!

在 sleep()中停止线程

如果线程在 sleep()状态下停止线程,会有什么效果呢?我们来看下面的示例:

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
public class SleepInterrupt extends Thread{

@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止!进入catch!"+this.isInterrupted());
e.printStackTrace();
}
}


public static void main(String[] args) {

try {
SleepInterrupt thread=new SleepInterrupt();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}

System.out.println("end");

}

}

执行结果如下:

1
2
3
4
5
6
run begin
end
在沉睡中被停止!进入catch!false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.thread.chapter1.SleepInterrupt.run(SleepInterrupt.java:18)

从执行结果来看,如果在 sleep 状态下停止某一线程,会进入 catch 语句,并且清除停止状态值,使之变为 false。上面的示例是先 sleep 然后再用 interrupt()停止,与之相反的操作也要注意。

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
public class SleepInterrupt2 extends Thread {

@Override
public void run() {
super.run();
try {
for (int i = 0; i < 100000; i++) {
System.out.println("i=" + (i + 1));
}
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到了sleep!进入catch!");
e.printStackTrace();
}
}


public static void main(String[] args) {
SleepInterrupt2 thread = new SleepInterrupt2();
thread.start();
thread.interrupt();
System.out.println("end");

}

}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
......
i=99991
i=99992
i=99993
i=99994
i=99995
i=99996
i=99997
i=99998
i=99999
i=100000
run begin
先停止,再遇到了sleep!进入catch!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.thread.chapter1.SleepInterrupt2.run(SleepInterrupt2.java:21)

使用 return 停止线程

将方法 interrupted()与 return 结合使用也能实现停止线程的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ReturnInterrupt extends Thread {

@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("time=" + System.currentTimeMillis());
}
}

public static void main(String[] args) throws InterruptedException {
ReturnInterrupt thread=new ReturnInterrupt();
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
......
time=1536809054172
time=1536809054172
time=1536809054172
time=1536809054172
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
停止了!

小结

上面几种方法都是通过使用 interrupt 停止线程,只是做了不同的技巧处理,不过建议使用“抛异常”的方法来实现线程的停止,因为在 catch 块中还可以将异常向上抛,使线程停止的事件得以传播。

参考

《java 多线程编程核心技术》

思考 3: 任何事情都有代价

选择越多,选择越好

没得选也不要盲目

https://www.bilibili.com/video/av38824746

任何事情都有代价

选择难的事情

选择贵的东西

https://www.bilibili.com/video/av38272155

恨一个人总是容易,随便找个理由,大可弃他而去,爱一个人总是那么难,就算他于你而言只是个意外,就算有反对的声音,就算有人冷嘲热讽,你还是无法弃之不顾,还是常常奋不顾身,即便经过这一切,他还是惦记着你的不好,你也不过一声叹息。–S01E07

https://www.bilibili.com/video/av46310847

人生充满谎言

人心浮动,坚守初心

真正想要的一件不拉的全丢光了

死亡的本身并没有意义

活在当下

https://www.bilibili.com/video/av34173276

一个人最大优点,往往藏着他最大的弱点

https://www.bilibili.com/video/av38041629

见识过这世界的疯狂,才明白什么叫做无常

S01E08

https://www.bilibili.com/video/av38531844

没得到的不一定是好的

一个早早离开的人,安放着你最美好的想像

“人的出现是没有目的的,你的存在也不需要任何意义,不过是众多时间线里一个选择的结果。你也不用纠结另一种可能的生活,你之所以成为你就是你之前的的无数选择。偏差一点点就变成了另一个世界的故事。就像埋在花园里的爷孙俩,那和你并没有关系。你的人生是唯一而且确定的,它就在眼前。”
“最向往的生活永远在别处,这是一种求而不得的无奈,而这世间最无奈的就是就是将‘拥有’和‘没有’做比较。一处不存在的生活,一个早已离开的人安放着你最美好的想象,没有谁敌得过想象。”

https://www.bilibili.com/video/av67233866

标签效应:领导会给人贴标签

少点标签

自证预言

强者愈强

https://www.bilibili.com/video/av53942294

少犯错,少拖延

7件希望20岁的自己就开始做的事

视频作者:是落拓啊

## 1.对自己负责任,投资自己

每一选择都在影响你的未来,不要害怕承担你的责任。

2.好好学习

软实力,高标准,决定对自己负责

3.珍惜时间,设计目标

人生的时间很短,和喜欢的东西一起浪费的时间更像时间。

4.行动

看完书好像是得到了什么,但是没有,行动是很重要的,实践,持续坚持,赶紧行动。

5.关注健康(锻炼,饮食/营养,休息)

身体是一切的基础,多锻炼

6.学会拒绝

不要推着被走

7.记录生活

拿上相机记录你的生活,可以看看以前的自己,十年之后是很宝贵的记忆。

线程池

原文来自 《码出高效:Java开发手册》

线程池的好处

  • 利用线程池管理并复用线程、控制最大并发数等。
  • 实现任务线程队列缓存策略和拒绝机制
  • 实现某些与时间相关的功能,如定时执行、周期执行等
  • 隔离线程环境。比如,交易服务和搜索服务在同一台服务器上,分别开启两个线程池,交易线程的资源消耗明显更大;因此,通过配置独立的线程池,将较慢的交易服务与搜索服务隔离开,避免各服务线程相互影响。

思考 2: 难度递增原则🤔

个人思考,可能不太正确

事情的难度是逐渐递增的

🌰例子:

互联网的招聘要求越来越高

生活成本越来越高

房价越来越高

可能跟熵增加原理有关,因此必须有外力减少熵值,让事情变得稳定有序。

注: 熵增加原理可以参考下面👇的文章

熵的社会学意义

要求高不一定坏事

要求高的工作反而可能会简单些,比如在小公司什么都要干,大公司只需要专注一个方面,并且大公司培养制度比较完善。

后来者居上

比如以前都是使用SSM框架比较麻烦,后来使用Spring Boot框架,学习成本更低,如果不学习不进步,就会被淘汰。

工具推荐 1: Postman-最常用的接口测试工具

参考文章:

Postman安装与使用

Postman 安装及使用入门教程

Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。

官方网站:https://www.getpostman.com/

1. 安装

Postman下载地址: https://www.getpostman.com/apps

主界面

2. 测试

2.1 简单的Get请求:

2.2 简单的POST请求

POST:HTTP的常用请求方法。

Body:设置POST请求的参数。

  • form-data: HTTP请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。
  • x-wwww-form-urlencode:HTTP请求中的application/x-www-from-urlencoded,会将表单内的数据转换为键值对。
  • raw:可以发送任意格式的接口数据,可以text、json、xml、html等。
  • binary:HTTP请求中的相Content-Type:application/octet-stream,只可以发送二进制数据。通常用于文件的上传。

2.3 认证接口

Authorization:用于需要认证的接口。
Basic Auth:最基本的一种认证类型,还有OAuth 1.0/2.0、Digest Auth等认证类型。
Username/Password:这是针对Basic Auth类型的认证的用户名/密码,并非我们认为的系统登录的用户名密码。

3. 界面简单介绍

标记出来的几个按钮,从上到下、从左至右的顺序依次是 导入、新建文件夹、保存请求、下载

  • 导入:用于导入你或团队保存的API请求文件,json格式。
  • 新建文件夹:用于API请求分门别类,便于管理。
  • 保存请求:保存你的API请求,返回值也能存储下来。
  • 下载:下载你测试通过的API请求,团队共享,导入。json格式,可手动编辑的

年轻人,你认命吗?

原文作者: 大飞码字

原文地址: https://mp.weixin.qq.com/s/h_TR7kH1YQzsN6d9sJwZrA

信命,但我不认命,加油 ⛽️

每个优秀的人,都有一段沉默的时光。那段时光,是付出了很多努力,却得不到结果的日子,我们把它叫做扎根。—- Feily Zhang 留言

最近有一位同学留言,大意是说:时代不同了,你们这辈人(80后)赶上了时代的红利,而我们这些新人(90后) 已经吃不到红利,再怎么努力也改变不了人生了。

我看到过好几次类似的留言了,这种逻辑也对,也错,或者本没有什么对错。这是一种人生态度,甚至可以说是一种信念,有时候你信,就会成真,如果你坚信未来没有机会了,那大概率对你来说,可能真的没有机会了。

前段时间有篇爆火的文章《大江大河40年:改变命运的七次机遇》,里面历数了过去40年的七次大机遇,每次大机遇都会给一拨人带来巨大的红利,甚至直接带来阶层的跃迁。

80后同学赶上的是PC互联网红利,移动互联网红利和房地产后期红利,90后同学只赶上了移动互联网后期红利,地产红利大部分90后可能没赶上,未来还有没有,也不知晓了。像70后的前辈们,他们赶上的是整体互联网兴起的红利,还有地产的全盘红利。

这么来看,确实有种,一代不如一代的感觉。

中国经济在过去的40年里面,获得了飞速的发展,到目前为止,经济依然维持了6个点的增速,虽然没有前几年高了, 但对比其它国家2个点,1个点甚至是负增长,中国已经是非常厉害了。

我不觉得国内经济的腾飞立马就要结束了,就算是增速不如以前,但依然还在上升之中,我觉得未来依然蕴藏着大量的机会。

有些同学没有赶上移动互联网的红利,没有赶上地产的红利,就开始信命了,觉得自己的命不好,觉得自己命中注定吃不到这些红利。

我也活了三十好几了,我不完全否定命的说法,命可以理解为运气或者是大运气吧,每个人身上都存在。但这种东西,我们无法去改变,也无法预知,甚至乎,在你的生命没有结束之前,你都无法判断自己的命是好还是不好。

一生长着呢,年轻人,怎么能那么快就认命了呢?

我不能说自己经历过什么大风大浪,但一路走来,也并非一帆风顺。

初中考试,以全县第十的成绩考入我们市第一的高中,那是我的高光时刻。但我高中过得一塌糊涂,最后只考了一个二本。当时有一个远房亲戚,跟我一同考入市高中,当时我的分数比他高了70几分(总分650,我是612), 高考的时候,他考到了中大,我只考到一个二本。

这种巨大落差带来的打击,是可以直接击垮一个人的,按理,我的人生有可能就此一蹶不振了,在二本里面浑浑噩噩,最后毕业可能连工作都找不到。

但我不服,我不认命,大学四年,我拼命的学,我可以毫不谦虚的说,我大学四年的努力程度要远超很多一本的学生。最后进入了腾讯,当时我们学院1000多人,跟我一同进腾讯的只有5个。

如果我当时认命了,就不会有你现在看到的文章了。

工作之后,我也没有表现的差劲,超越了很多一本或研究生毕业的同学。中间过程中,有人来,有人走,有人受不了加班,受不了压力离开了,我也一路扛了下来。

在工作了十年之后,我开始写起了公众号,我也不知道未来会怎样,但我依然不认命,我相信命运的存在,但我要尽我最大的努力去控制我的人生,我相信,自己的人生是被自己创造出来的,不是随波逐流的,虽然我无法对抗历史的洪流,但我依然可以在人力所能极的范围内做到最好。

这就是我的人生信条,很多事情,包括自己的未来,不信是一定不会有的,信了,虽然未必会有,但心里有信念,你的人生才会有希望!

机遇和能力是相辅相成的,先得有能力,机遇出现的时候,才能够抓住,而个人又会在捉住的机遇中获得新的成长。所以得先有了能力,才可能抓住机遇,这是显而易见的道理,但当你认命,认为自己命中不会有这些机遇,你就容易放弃自己。

从过去40几年发展的情况来看,随着中国经济的发展,每隔几年就会出现一波新的红利,目前的经济远未到衰落的时候,所以未来的红利依然存在,但捉住红利不是一件容易的事情,有不少人因为自身没有准备好,机遇到自己身边的时候也无法抓住,这就极其可惜了。

有人信:“三分天注定,七分靠打拼!”

有人信:“七分天注定,三分靠打拼!”

没有对,也没有错,不同的人,会信奉不同的人生信条,不同的信念也造就了不同的人生。

你问我信什么?

我信命,但我不认命,只要还能活着,我就不会放弃努力!

Github+docsify打造个人文档

原文地址: Github+docsify打造个人文档

1. 简介

在日常开发中 前后端对接时 经常要写很多文档Api。docsify就是一个强大的文档生成工具 界面清新好 支持语法高亮和Markdown 语法,并且docsify 扩展了一些 Markdown 语法可以让文档更易读。像vue.js官网(cn.vuejs.org/)就是 docsify 其中的一种主题 并且是目前用的的最多的主题

2. 效果图如下

预览链接:http://gaohanghang.github.io/docsify/

3. 快速开始

首先先安装好npm和nodejs,这里就不做过多介绍了 自行安装即可

安装docsify 推荐安装 docsify-cli 工具,可以方便创建及本地预览文档网站。

1
npm i docsify-cli -g

初始化项目

1
2
# 进入指定文件目录 如下:F:\ideWork\githubWork\test_docs 
运行 docsify init ./docs

初始化成功后,可以看到 ./docs 目录下创建的几个文件

1
2
3
index.html 入口文件
README.md 会做为主页内容渲染
.nojekyll 用于阻止 GitHub Pages 会忽略掉下划线开头的文件

4. 本地预览网站

运行一个本地服务器通过 docsify serve 可以方便的预览效果,而且提供 LiveReload 功能,可以让实时的预览。默认访问 http://localhost:3000

1
docsify serve docs

一个基本的文档网站就搭建好了,docsify还可以自定义导航栏,自定义侧边栏以及背景图和一些开发插件等等 更多配置请参考官方文档 https://docsify.js.org/#/zh-cn/quickstart

5. 下面介绍docsify如何部署到Github Pages

和 GitBook 生成的文档一样,我们可以直接把文档网站部署到 GitHub Pages 或者 VPS 上

5.1 GitHub Pages

GitHub Pages 支持从三个地方读取文件

  • docs/ 目录
  • master 分支
  • gh-pages 分支

上传文件至Github仓库 官方推荐直接将文档放在 docs/ 目录下,在设置页面开启 GitHub Pages 功能并选择 master branch /docs folder 选项。

选择了之后就可以访问个人文档了。

官方文档:https://docsify.js.org/#/zh-cn/quickstart

预览链接:http://gaohanghang.github.io/docsify/

  • Copyrights © 2015-2023 高行行
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信