并发编程 - 线程基础篇

一、并发的发展历史

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进行重置。

原创文章 88 获赞 21 访问量 3万+

猜你喜欢

转载自blog.csdn.net/cfy1024/article/details/95905855