其他分享
首页 > 其他分享> > 多线程高并发递进梳理

多线程高并发递进梳理

作者:互联网

  逆向APP的核心目的之一就是写爬虫爬取后台的数据,诸如电商、评论、弹幕等;另一个目的就是提供sign字段的生成服务,可以通过https服务接口的形式给第三方调用!不论是做啥,为了提高效率,多线程都是必须的!可一旦涉及到多线程,线程之间的同步和互斥就必须考虑了,包括生产者和消费者之间、生产者和生产者之间、消费者和消费者之间的同步或互斥!为了解决这些问题,java逐步推出了syncronized、volatile、各种list/array、map、线程池ThreadPool等接口,内容巨多,是时候提炼、总结一下这些功能或接口的作用了!

  1、先看看最简单的一种情况:多线程用的最多的就是这种生产、消费则模型了!凡是学过软件编程的都懂:生产者往共享内存(一般是某种特定的数据结构,比如链表、数组等)写数据,消费者从共享内存读数据!

        

     (1)为了让生产者和消费者之间同步或互斥,不至于往共享内存读写错误的数据,java发明了两个“关键字”:syncronized和volatile!

   (2) 多个线程同时工作,互相分工协作是必然的,光有锁代码、同步变量还不够啊,线程之间怎么互相联络通信了?好比一个团队有很多成员,成员之间肯定要沟通交流吧?还没有哪个大项目是一个人能搞定的,分工协作是必然,所以线程之间也涉及到通信,java又是怎么做的了?

  2、上述就是最简单、最基础的生产者和消费者模型!如果让开发人员自己用多线程“同时”读写list、array等数据结构,无疑很麻烦:因为开发人员自己要考虑同步和互斥,涉及到syncronized和volatile的使用;为了方便开发在多线程安全的情况下轻松加愉快地读写常见的数据结构,java又推出了很多在多线程情况下安全的数据结构(其实就是在读写数据的关键代码前面加上syncronized和voltile,把所有应用开发人员都要做的重复性质的工作都抽象出来全做了):vector、hashTable、各种queue(Dqueue、blockingQueue、syncronizedQueue、transferQueue)、disruptor等!各种层出不穷数据结构的本质区别如下:

  经过java大牛们提供的各种线程安全数据结构后,开发人员终于不用考虑线程同步、互斥的情况了,可以直接读写队列的数据了,现在的生产、消费者模型如下了:

      

  3、事已至此,多线程的开发就万事大吉了么?虽然中间的共享队列实现了多线程安全,还有线程本身的调度怎么解决了?举一些常见的例子:

  .....................

       以上所有的工作如果都靠开发者手动实现,每个开发人员都要重复干这些事,整体的开发效率可想而知!java顺势而为,推出了大杀器: ThreadPool线程池!

  (1)创建线程池也很简单,调用一个线程的API就够了,demo如下:

ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 4, //核心线程和最大线程
                60, TimeUnit.SECONDS,//线程池的生存时间和单位
                //不同的queue会产生不同的线程池;比如syncronizedQueue:来一个任务必须马上处理一个
                new ArrayBlockingQueue<Runnable>(4),//任务队列,这里只能装4个任务
                Executors.defaultThreadFactory(),//线程工厂:线程名
                new ThreadPoolExecutor.CallerRunsPolicy());//main主线程自己执行其中一个任务

  线程池有7个核心的参数,确定了线程池的大小、类型、执行任务的方式等,分别解释如下:

  (2)所谓的task,本质就是一段需要被执行的代码,通常是开发人员根据业务需求而定制的代码。具体到coding层面,一般都是implement Runable接口,把业务逻辑代码放入run方法,比如下面这种:

static class Task implements Runnable {
        private int i;

        public Task(int i) {
            this.i = i;
        }
        /*所谓的task,本质就是一段需要被执行的代码*/
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Task " + i);
            try {
                //任务是阻塞的,所以线程池的4个线程全占用了;任务队列也占用了
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

   然后主线程中就可以愉快地给线程池添加task去执行了:

for (int i = 0; i < 8; i++) {//线程池执行8个线程
            tpe.execute(new Task(i));
        }

    整个过程地逻辑还是很简单、清晰和明了的!图示如下: 

   

 

   有m个的thread去执行n个task;task被放在queue等待thread取出来执行!根据不同取task的策略又分成了:

   (3)学过操作系统或汇编指令的同学都了解:

  最后要点总结如下:

 

 

参考:

1、https://github.com/alibaba/p3c/blob/master/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf   alibaba的java开发手册

2、https://github.com/bjmashibing/JUC  马老师的多线程和高并发代码

 

标签:执行,task,队列,代码,递进,并发,线程,多线程
来源: https://www.cnblogs.com/theseventhson/p/16497502.html