一、并发的发展历史
1、真空管 / 穿孔打卡
工作流:把程序写在纸上,打成卡片,把卡片的程序输入到计算机上,电脑运行出结果打印到打印机上。
最大的问题,计算机在等程序输入的时候,会处于空闲状态。
2、晶体管 / 批处理操作系统
工作流:把程序写在卡片上,读入到磁盘机上,电脑通过读取磁盘机上的内容进行运行,将结果输入到打印机上。
解决了计算机空闲问题,计算机会一直处于运行状态。
带来的问题:IO问题,IO阻塞会影响其他程序的运行,造成CPU资源浪费。
3、集成电路 / 多道程序设计
(1)、引入进程概念,进程之间相互隔离,计算机可以加载多个进程,如果有进程阻塞,可以快速切换成其他进程,CPU通过时间片快速切换进程来实现多个进程“并行”的假象(单核CPU哈)。
(2)、CPU从单核发展到多核,可以真正并行的执行多个进程。而且,一个进程可能会有多个任务,而这些任务并不是串行的关系,需要并行执行,所以就引出了线程的概念。线程是一种轻量级的进程,其创建、销毁和切换的成本都会非常小。
其实有一种,硬件的更新换代带动了软件的发展的感jio。
二、Java线程的创建方式
1、Runabel 接口
2、Thread类(本质上是对Runabel接口的实现)
3、Callable / Future 带返回值的线程
4、ThrealPool 线程池
线程可以合理的利用多核CPU资源,提高程序的吞吐量,提升对计算机资源的利用。
三、线程在实际中的应用
1、文件批跑,对账等场景,通过线程去跑。
2、BIO模型优化。
可能会存在一些阻塞,如:
socket socket = socket.accept(); / /连接阻塞
socket.inputstream(); // read阻塞
socket.outputstream(); // write阻塞
利用线程解决读写阻塞,如下:
new Therad(new handler(socket)).start(); // 可以解决r/w阻塞问题
3、zookeeper源码
模拟zookeeper异步处理思想小demo
public interface RequestProcessor {
void process(Request request);
}
/**
* @description:预处理
* @author: annecheng,2019-07-14 18:46
*/
public class PrevProcessor extends Thread implements RequestProcessor{
LinkedBlockingDeque<Request> requests = new LinkedBlockingDeque<Request>(); //阻塞队列
private RequestProcessor nextProcessor;
private volatile boolean isFinish = false;
public PrevProcessor() {
}
public PrevProcessor(RequestProcessor nextProcessor) {
this.nextProcessor = nextProcessor;
}
public void shutdoen(){ //对外提供关闭的方法
isFinish = true;
}
/**
*@Description: 不断的去取数据
*@Author: annecheng,2019/7/14
*/
@Override
public void run() {
while (!isFinish){
try {
Request request = requests.take(); // 阻塞式获取数据
// 真正的处理逻辑
System.out.println("PrevProcessor :" + request);
//处理完了之后交给下一个责任链
if(nextProcessor != null) {
nextProcessor.process(request);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void process(Request request) {
//验证请求参数,根据实际需求做处理
requests.add(request);
}
}
/**
* @description:保存
* @author: annecheng,2019-07-14 18:46
*/
public class SaveProcessor extends Thread implements RequestProcessor{
LinkedBlockingDeque<Request> requests = new LinkedBlockingDeque<Request>(); //阻塞队列
private RequestProcessor nextProcessor;
private volatile boolean isFinish = false;
public SaveProcessor() {
}
public SaveProcessor(RequestProcessor nextProcessor) {
this.nextProcessor = nextProcessor;
}
public void shutdoen(){ //对外提供关闭的方法
isFinish = true;
}
/**
*@Description: 不断的去取数据
*@Author: annecheng,2019/7/14
*/
@Override
public void run() {
while (!isFinish){
try {
Request request = requests.take(); // 阻塞式获取数据
// 真正的处理逻辑
System.out.println("SaveProcessor :" + request);
//处理完了之后交给下一个责任链
if(nextProcessor != null) {
nextProcessor.process(request);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void process(Request request) {
//验证请求参数,根据实际需求做处理
requests.add(request);
}
}
/**
* @description:打印
* @author: annecheng,2019-07-14 18:46
*/
public class PrintProcessor extends Thread implements RequestProcessor{
LinkedBlockingDeque<Request> requests = new LinkedBlockingDeque<Request>(); //阻塞队列
private RequestProcessor nextProcessor;
private volatile boolean isFinish = false;
public PrintProcessor(RequestProcessor nextProcessor) {
this.nextProcessor = nextProcessor;
}
public PrintProcessor() {
}
public void shutdoen(){ //对外提供关闭的方法
isFinish = true;
}
/**
*@Description: 不断的去取数据
*@Author: annecheng,2019/7/14
*/
@Override
public void run() {
while (!isFinish){
try {
Request request = requests.take(); // 阻塞式获取数据
// 真正的处理逻辑
System.out.println("PrintProcessor :" + request);
//处理完了之后交给下一个责任链
if(nextProcessor != null) {
nextProcessor.process(request);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void process(Request request) {
//验证请求参数,根据实际需求做处理
requests.add(request);
}
}
测试代码:
public class App {
static RequestProcessor requestProcessor;
public void setUp() {
// 构造责任链 预处理 --》保存 --》打印 并启动
PrintProcessor printProcessor = new PrintProcessor();
printProcessor.start();
SaveProcessor saveProcessor = new SaveProcessor(printProcessor);
saveProcessor.start();
requestProcessor = new PrevProcessor(saveProcessor);
((PrevProcessor)requestProcessor).start();
}
public static void main(String[] args) {
App app = new App();
app.setUp();
// 构造责任链
Request request = new Request();
request.setName("Anne");
requestProcessor.process(request);
}
}
当请求过来的时候,是一个责任链模式,本该是串行的,但是每个责任链又利用了阻塞队列和线程来处理。
如果非常多的请求需要通过这个责任链去做处理的时候,可以采用这种方式,做到最大性能优化。
总结:可以利用异步消息队列改造自己的程序实现异步处理。
四、并发基础
4.1 生命周期
线程一共有6种状态,以及它们之间的关系图如下:
代码示例
public class ThreadStatusDemo {
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (true) {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Time_Waiting_Thread").start();
new Thread(() -> {
while (true) {
synchronized (ThreadStatusDemo.class) {
try {
ThreadStatusDemo.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "Waiting_Thread").start();
new Thread(new BlockedDemo(),"Block01_Thread").start();
new Thread(new BlockedDemo(),"Block02_Thread").start();
System.out.println("hhh");
}
static class BlockedDemo extends Thread {
@Override
public void run() {
synchronized (BlockedDemo.class){
while (true){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
可以看到各个线程的状态如下:
4.2 使用线程的目的
可以提高程序性能
4.3 线程的启动为什么是start?
而这个start0()在jvm,它会做两个比较重要的事情
1、new一个JavaThread();(这个里面会调用操作系统的创建线程 os::create_thread())
2、调用Thread::start()去启动刚才new好的javaThread,并且会把状态置为RUNNABLE;
所以,线程的start是基于操作系统去构造一个线程的启动,会回调run方法,然后把状态置为RUNNABLE;
4.4 线程的终止
不是stop(),不建议使用,已过期。
(1)、Thread.interrupt()
public class ThreadInterruptDemo {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
// 如果不是中断状态,就一直
while (!Thread.currentThread().isInterrupted()){ // 默认是false
i ++;
}
System.out.println("i = " + i);
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); //把线程终止 会把Interrupted设置成true
}
}
测试结果:
而如果不加 thread.interrupt(); //把线程终止 会把Interrupted设置成true
则整个线程就会陷入死循环。
底层代码:
而这个interrupt0(),会调用Thread::interrupt(),然后再调用os::interrupt()
(2)、run方法里增加开关,如下的isFinish字段
public void run() {
while (!isFinish){
// 业务逻辑
}
}
4.5 线程复位
第一种:Thread.interrupted(); // 复位,回到原始状态
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
// 如果不是中断状态,就一直
while (true)
if (Thread.currentThread().isInterrupted()) {
System.out.println("before:" + Thread.currentThread().isInterrupted());`在这里插入代码片`
Thread.interrupted(); // 复位,回到原始状态
System.out.println("after:" + Thread.currentThread().isInterrupted());
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); //把线程终止 会把Interrupted设置成true
}
第二种:异常
public class ThreadResetDemo2 {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
// 如果不是中断状态,就一直
while (!Thread.currentThread().isInterrupted()){
try {
TimeUnit.SECONDS.sleep(1); //中断一个处于阻塞状态的线程,会抛出异常,这个异常会把线程状态复位
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("i = " + i);
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); //把线程终止 会把Interrupted设置成true
System.out.println(thread.isInterrupted());
}
}
如上,异常会把线程的中断标记位置成false;
所有的阻塞相关的方法,都会抛出一个InterruptedException。
一般的阻塞方法都需要等到外部条件来触发释放阻塞,这样,可能会出现等不到外部条件来触发,所以它允许一个线程去停止当前做的事情,复位线程,并发送信号抛出(也就是InterruptedException做的事情)。
总结:
1,线程的启动,start -》基于不同的操作系统来实现不同线程创建和启动指令;
2,interrupt() ,线程终止
3,可以通过Thread.interrupted(),重置;
4,可以通过InterruptedException进行重置。