并发包

java.util.concurrent从jdk1.5开始新加入的一个包,致力于解决并发编程的线程安全问题,使用户能够更为快捷方便的编写多线程情况下的并发程序。

同步容器

同步容器只有包括Vector和HashTable,相比其他容器类只是多用了Synchronize的技术

Vector与ArrayList区别

1.ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。

2.Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢

注意: Vector线程安全、ArrayList线程不安全

HasTable与HasMap区别

1.HashMap不是线程安全的

HastMap是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap允许null key和null value,而hashtable不允许。

2.HashTable是线程安全的一个Collection。

3.HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。

HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。

HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。

注意: HashTable线程安全,HashMap线程不安全。

synchronizedMap synchronizedList

Collections.synchronized*(m) 将线程不安全集合变为线程安全集合

Map m = Collections.synchronizedMap(new HashMap());  List l = Collections.synchronizedList(new ArrayList());

ConcurrentHashMap

ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成了16个段(Segment)也就是最高支持16个线程的并发修改操作。

这也是在重线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好。

并发容器

CountDownLatch

CountDownLatch是JAVA提供在java.util.concurrent包下的一个辅助类,可以把它看成是一个计数器,其内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器,CountDownLatch通过构造函数传入一个初始计数值,调用者可以通过调用CounDownLatch对象的cutDown()方法,来使计数减1;如果调用对象上的await()方法,那么调用者就会一直阻塞在这里,直到别人通过cutDown方法,将计数减到0,才可以继续执行。

public class Test002 {  public static void main(String[] args) throws InterruptedException {    System.out.println("等待子线程执行完毕...");    CountDownLatch countDownLatch = new CountDownLatch(2);    new Thread(new Runnable() {      @Override      public void run() {        System.out.println("子线程," +Thread.currentThread().getName() + "开始执行...");        countDownLatch.countDown();// 每次减去1        System.out.println("子线程," + Thread.currentThread().getName() + "结束执行...");      }    }).start();    new Thread(new Runnable() {      @Override      public void run() {        System.out.println("子线程," + Thread.currentThread().getName() + "开始执行...");        countDownLatch.countDown();        System.out.println("子线程," + Thread.currentThread().getName() + "结束执行...");        }      }).start();      countDownLatch.await();// 调用当前方法主线程阻塞 countDown结果为0, 阻塞变为运行状态      System.out.println("两个子线程执行完毕....");      System.out.println("继续主线程执行..");    }}

CyclicBarrier

一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

使用场景

需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。

public class CyclicBarrierTest {  public static void main(String[] args) throws IOException, InterruptedException {    //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去    //Waits until all parties have invoked await on this barrier.     CyclicBarrier barrier = new CyclicBarrier(3);    ExecutorService executor = Executors.newFixedThreadPool(3);    executor.submit(new Thread(new Runner(barrier, "1号选手")));    executor.submit(new Thread(new Runner(barrier, "2号选手")));    executor.submit(new Thread(new Runner(barrier, "3号选手")));    executor.shutdown();  }}class Runner implements Runnable {  // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)  private CyclicBarrier barrier;  private String name;  public Runner(CyclicBarrier barrier, String name) {    super();    this.barrier = barrier;    this.name = name;  }  @Override  public void run() {    try {      Thread.sleep(1000 * (new Random()).nextInt(8));      System.out.println(name + " 准备好了...");      // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。      barrier.await();    } catch (InterruptedException e) {      e.printStackTrace();    } catch (BrokenBarrierException e) {      e.printStackTrace();    }    System.out.println(name + " 起跑!");  }}

Semaphore

Semaphore是计数信号量。Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个release方法增加一个许可证,这可能会释放一个阻塞的acquire方法。然而,其实并没有实际的许可证这个对象,Semaphore只是维持了一个可获得许可证的数量。

Semaphore经常用于限制获取某种资源的线程数量

需求: 一个厕所只有3个坑位,但是有10个人来上厕所,那怎么办?假设10个人的编号分别为1-10,并且1号先到厕所,10号最后到厕所。那么1-3号来的时候必然有可用坑位,顺利如厕,4号来的时候需要看看前面3人是否有人出来了,如果有人出来,进去,否则等待。同样的道理,4-10号也需要等待正在上厕所的人出来后才能进去,并且谁先进去这得看等待的人是否有素质,是否能遵守先来先上的规则。

class Parent implements Runnable {  private String name;  private Semaphore wc;  public Parent(String name,Semaphore wc){    this.name=name;    this.wc=wc;  }  @Override  public void run() {    try {      // 剩下的资源(剩下的茅坑)      int availablePermits = wc.availablePermits();      if (availablePermits > 0) {        System.out.println(name+"天助我也,终于有茅坑了...");      } else {        System.out.println(name+"怎么没有茅坑了...");      }      //申请茅坑 如果资源达到3次,就等待      wc.acquire();      System.out.println(name+"终于轮我上厕所了..爽啊");        Thread.sleep(new Random().nextInt(1000)); // 模拟上厕所时间。      System.out.println(name+"厕所上完了...");      wc.release();    } catch (Exception e) {    }  }}public class TestSemaphore02 {  public static void main(String[] args) {    Semaphore semaphore = new Semaphore(3);    for (int i = 1; i <=10; i++) {       Parent parent = new Parent("第"+i+"个人,",semaphore);       new Thread(parent).start();    }  }}

并发队列

ConcurrentLinkedQueue

ConcurrentLinkedQueue : 是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue.它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不允许null元素。

add 和offer() 都是加入元素的方法(在ConcurrentLinkedQueue中这俩个方法没有任何区别) poll() 和peek() 都是取头元素节点,区别在于前者会删除元素,后者不会。

public class ConcurrentLinkedQueueTest {

private static ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<Integer>();

private static int count = 2; // 线程个数

//CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

private static CountDownLatch latch = new CountDownLatch(count);  public static void main(String[] args) throws InterruptedException {    long timeStart = System.currentTimeMillis();    ExecutorService es = Executors.newFixedThreadPool(4);    ConcurrentLinkedQueueTest.offer();    for (int i = 0; i < count; i++) {      es.submit(new Poll());    }    latch.await(); //使得主线程(main)阻塞直到latch.countDown()为零才继续执行    System.out.println("cost time " + (System.currentTimeMillis() - timeStart) + "ms");    es.shutdown();  }  /**   * 生产   */  public static void offer() {    for (int i = 0; i < 100000; i++) {      queue.offer(i);    }  }  static class Poll implements Runnable {    public void run() {      // while (queue.size()>0) {      while (!queue.isEmpty()) {        System.out.println(queue.poll());      }      latch.countDown();    }  }}

BlockingQueue

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:

在队列为空时,获取元素的线程会等待队列变为非空。

当队列满时,存储元素的线程会等待队列可用。

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素

ArrayBlockingQueue

ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。

ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。

LinkedBlockingQueue

LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表

SynchronousQueue

SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费

public class BlockingQueueTest2 {  /**   *    * 定义装苹果的篮子   *    */  public class Basket {    // 篮子,能够容纳3个苹果    BlockingQueue<String> basket = new LinkedBlockingQueue<String>(3);    // 生产苹果,放入篮子    public void produce() throws InterruptedException {      // put方法放入一个苹果,若basket满了,等到basket有位置      basket.put("An apple");    }    // 消费苹果,从篮子中取走    public String consume() throws InterruptedException {      // take方法取出一个苹果,若basket为空,等到basket有苹果为止(获取并移除此队列的头部)      return basket.take();    }  }  // 定义苹果生产者  class Producer implements Runnable {    private String instance;    private Basket basket;    public Producer(String instance, Basket basket) {      this.instance = instance;      this.basket = basket;    }    public void run() {      try {        while (true) {          // 生产苹果          System.out.println("生产者准备生产苹果:" + instance);          basket.produce();          System.out.println("!生产者生产苹果完毕:" + instance);          // 休眠300ms          Thread.sleep(300);        }      } catch (InterruptedException ex) {        System.out.println("Producer Interrupted");      }    }  }  // 定义苹果消费者  class Consumer implements Runnable {    private String instance;    private Basket basket;    public Consumer(String instance, Basket basket) {      this.instance = instance;      this.basket = basket;    }    public void run() {      try {        while (true) {          // 消费苹果          System.out.println("消费者准备消费苹果:" + instance);          System.out.println(basket.consume());          System.out.println("!消费者消费苹果完毕:" + instance);          // 休眠1000ms          Thread.sleep(1000);        }      } catch (InterruptedException ex) {        System.out.println("Consumer Interrupted");      }    }  }  public static void main(String[] args) {    BlockingQueueTest2 test = new BlockingQueueTest2();    // 建立一个装苹果的篮子    Basket basket = test.new Basket();    ExecutorService service = Executors.newCachedThreadPool();    Producer producer = test.new Producer("生产者001", basket);    Producer producer2 = test.new Producer("生产者002", basket);    Consumer consumer = test.new Consumer("消费者001", basket);    service.submit(producer);    service.submit(producer2);    service.submit(consumer);    // 程序运行5s后,所有任务停止//    try {//      Thread.sleep(1000 * 5);//    } catch (InterruptedException e) {//      e.printStackTrace();//    }//    service.shutdownNow();  }}

PriorityBlockingQueue

优先级阻塞队列,该实现类需要自己实现一个继承了 Comparator 接口的类, 在插入资源时会按照自定义的排序规则来对资源数组进行排序。 其中值大的排在数组后面 ,取值时从数组头开始取

public class TestQueue{  static Logger logger = LogManager.getLogger();  static Random random = new Random(47);  public static void main(String args[]) throws InterruptedException  {    PriorityBlockingQueue<PriorityEntity> queue = new PriorityBlockingQueue<PriorityEntity>();    ExecutorService executor = Executors.newCachedThreadPool();    executor.execute(new Runnable()    {      public void run()      {        int i = 0;        while (true)        {          queue.put(new PriorityEntity(random.nextInt(10), i++));          try          {            TimeUnit.MILLISECONDS.sleep(random.nextInt(1000));          }          catch (InterruptedException e)          {            logger.error(e);          }        }      }    });    executor.execute(new Runnable()    {      public void run()      {        while (true)        {          try          {            System.out.println("take-- " + queue.take() + " left:-- [" + queue.toString() + "]");            try            {              TimeUnit.MILLISECONDS.sleep(random.nextInt(3000));            }            catch (InterruptedException e)            {              logger.error(e);            }          }          catch (InterruptedException e)          {            logger.error(e);          }        }      }    });    try    {      TimeUnit.SECONDS.sleep(5);    }    catch (InterruptedException e)    {      logger.error(e);    }  }  static class PriorityEntity implements Comparable<PriorityEntity>  {    private static int count = 0;    private int id = count++;    private int priority;    private int index = 0;    public PriorityEntity(int _priority, int _index)    {      System.out.println("_priority : " + _priority);      this.priority = _priority;      this.index = _index;    }    public String toString()    {      return id + "# [index=" + index + " priority=" + priority + "]";    }    //数字小,优先级高    public int compareTo(PriorityEntity o)    {      return this.priority > o.priority ? 1 : this.priority < o.priority ? -1 : 0;    }  }}

线程池

开发过程中,合理地使用线程池可以带来3个好处:

降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。

提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

线程池作用

线程池作用就是限制系统中执行线程的数量。

根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

线程池的分类

newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

public static ExecutorService newCachedThreadPool() {    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                   60L, TimeUnit.SECONDS,                   new SynchronousQueue<Runnable>());  }public class ThreadPoolExecutorTest {    public static void main(String[] args) {      ExecutorService cachedThreadPool = Executors.newCachedThreadPool();       for (int i = 0; i < 10; i++) {          final int index = i;          try {          Thread.sleep(index * 1000);          } catch (InterruptedException e) {          e.printStackTrace();          }          cachedThreadPool.execute(new Runnable() {          public void run() {             System.out.println(index);          }        });       }     }  }

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程

newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                   0L, TimeUnit.MILLISECONDS,                   new LinkedBlockingQueue<Runnable>());}public class ThreadPoolExecutorTest {    public static void main(String[] args) {      ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);      for (int i = 0; i < 10; i++) {        final int index = i;        fixedThreadPool.execute(new Runnable() {          public void run() {            try {              System.out.println(index);              Thread.sleep(2000);            } catch (InterruptedException e) {              e.printStackTrace();            }          }        });      }    }  }

因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()

newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

public ScheduledThreadPoolExecutor(int corePoolSize) {    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,       new DelayedWorkQueue());  }public class ThreadPoolExecutorTest {    public static void main(String[] args) {      ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);      scheduledThreadPool.schedule(new Runnable() {        public void run() {          System.out.println("delay 3 seconds");        }      }, 3, TimeUnit.SECONDS);    }  }

表示延迟3秒执行

public class ThreadPoolExecutorTest {    public static void main(String[] args) {      ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);      scheduledThreadPool.scheduleAtFixedRate(new Runnable() {        public void run() {          System.out.println("delay 1 seconds, and excute every 3 seconds");        }      }, 1, 3, TimeUnit.SECONDS);    }  }

表示延迟1秒后每3秒执行一次

newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

public static ExecutorService newSingleThreadExecutor() {    return new FinalizableDelegatedExecutorService      (new ThreadPoolExecutor(1, 1,                  0L, TimeUnit.MILLISECONDS,                  new LinkedBlockingQueue<Runnable>()));  }

阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

ThreadPoolExecutor  public ThreadPoolExecutor(int corePoolSize,               int maximumPoolSize,               long keepAliveTime,               TimeUnit unit,               BlockingQueue<Runnable> workQueue,               ThreadFactory threadFactory,               RejectedExecutionHandler handler)

corePoolSize - 线程池核心池的大小。

maximumPoolSize - 线程池的最大线程数。

keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

unit - keepAliveTime 的时间单位。

workQueue - 用来储存等待执行任务的队列。

threadFactory - 线程工厂。

handler - 拒绝策略。

线程优先级:

corePoolSize > workQueue > maximumPoolSize>handler(拒绝)

拒绝策略:

  1. CallerRunsPolicy :这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
  2. AbortPolicy :对拒绝任务抛弃处理,并且抛出异常。(默认使用的)
  3. DiscardPolicy :对拒绝任务直接无声抛弃,没有异常信息。
  4. DiscardOldestPolicy :对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个线程,然后把拒绝任务加到队列。

合理配置线程池

要想合理的配置线程池,就必须首先分析任务特性,可以从以下几个角度来进行分析:

  • 任务的性质:CPU密集型任务,IO密集型任务和混合型任务。
  • 任务的优先级:高,中和低。
  • 任务的执行时间:长,中和短。
  • 任务的依赖性:是否依赖其他系统资源,如数据库连接。
  • 任务性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务配置尽可能少的线程数量,如配置Ncpu+1个线程的线程池。IO密集型任务则由于需要等待IO操作,线程并不是一直在执行任务,则配置尽可能多的线程,如2*Ncpu。混合型的任务,如果可以拆分,则将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率,如果这两个任务执行时间相差太大,则没必要进行分解。我们可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
  • 优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先得到执行,需要注意的是如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行。
  • 执行时间不同的任务可以交给不同规模的线程池来处理,或者也可以使用优先级队列,让执行时间短的任务先执行。
  • 依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,如果等待的时间越长CPU空闲时间就越长,那么线程数应该设置越大,这样才能更好的利用CPU。

CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在执行任务 IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数 操作系统之名称解释: 某些进程花费了绝大多数时间在计算上,而其他则在等待I/O上花费了大多是时间,前者称为计算密集型(CPU密集型)computer-bound,后者称为I/O密集型,I/O-bound。

写在最后:欢迎留言讨论,加关注,持续更新!!!

阿里P7告诉你什么是java并发包、线程池、锁的更多相关文章

  1. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  2. Java并发包——线程池

    Java并发包——线程池 摘要:本文主要学习了Java并发包中的线程池. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520/p/3932921.html ...

  3. Java并发包线程池之Executors、ExecutorCompletionService工具类

    前言 前面介绍了Java并发包提供的三种线程池,它们用处各不相同,接下来介绍一些工具类,对这三种线程池的使用. Executors Executors是JDK1.5就开始存在是一个线程池工具类,它定义 ...

  4. Java并发包线程池之ForkJoinPool即ForkJoin框架(二)

    前言 前面介绍了ForkJoinPool相关的两个类ForkJoinTask.ForkJoinWorkerThread,现在开始了解ForkJoinPool.ForkJoinPool也是实现了Exec ...

  5. Java并发包线程池之ForkJoinPool即ForkJoin框架(一)

    前言 这是Java并发包提供的最后一个线程池实现,也是最复杂的一个线程池.针对这一部分的代码太复杂,由于目前理解有限,只做简单介绍.通常大家说的Fork/Join框架其实就是指由ForkJoinPoo ...

  6. Java并发包线程池之ScheduledThreadPoolExecutor

    前言 它是一种可以安排在给定的延迟之后执行一次或周期性执行任务的ThreadPoolExecutor.因为它继承了ThreadPoolExecutor, 当然也具有处理普通Runnable.Calla ...

  7. Java并发包--线程池原理

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import jav ...

  8. Java并发包--线程池框架

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509903.html 线程池架构图 线程池的架构图如下: 1. Executor 它是"执行者 ...

  9. Java并发包线程池之ThreadPoolExecutor

    参数详解 ExecutorService的最通用的线程池实现,ThreadPoolExecutor是一个支持通过配置一些参数达到满足不同使用场景的线程池实现,通常通过Executors的工厂方法进行配 ...

随机推荐

  1. c#子线程执行完怎么通知主线程(转)

    定义一个委托实现回调函数 public delegate void CallBackDelegate(string message); 程序开始的时候 //把回调的方法给委托变量 CallBackDe ...

  2. dubbo的负载均衡策略和容错策略

    一.dubbo 负载均衡策略 random loadbalance 默认情况下,dubbo 是 random load balance 随机调用实现负载均衡,可以对 provider 不同实例设置不同 ...

  3. 学习笔记:oracle之win10安装卸载oracle 11gR2步骤及常见问题解决

    1.win10下安装oracle11g 1.1 工具原料 oracle11g安装包(64位) 1.2 步骤方法 1.在Oracle官网下载安装包,下载后,得到的文件如图所示: 2.将两个文件进行解压缩 ...

  4. 【leetcode算法-简单】38. 报数

    [题目描述] 报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数.其前五项如下: 1. 12. 113. 214. 12115. 1112211 被读作  "one 1&qu ...

  5. TypeScript 类型推导及类型兼容性

    类型推导就是在没有明确指出类型的地方,TypeScript编译器会自己去推测出当前变量的类型. 例如下面的例子: let a = 1; 我们并没有明确指明a的类型,所以编译器通过结果反向推断变量a的类 ...

  6. centos7.6 yum安装mysql5.7版本

    由于mysql5.5及之前的版本一些项目上线报错 卸载: 首先删除centos上原来的mysql老版本,注意备份,清理干净. 之前怎么安装的清理,防止卸载不干净会有冲突. 我之前yum安装mysql5 ...

  7. keras损失函数详解

    以下信息均来自官网 ------------------------------------------------------------------------------------------ ...

  8. python_openCV例程遇到error: (-215) !empty() in function cv::CascadeClassifier::detectMultiScale的简单解决方法

    需要把haar分类器训练的结果xml数据放在名为haarcascades的文件夹下进行调用. 将: face_cascade = cv2.CascadeClassifier('haarcascade_ ...

  9. 利用sort对结构体进行排序

    我定义了一个学生类型的结构体来演示sort排序对结构体排序的用法 具体用法看代码 #include<iostream> #include<string> #include< ...

  10. Skip List(跳跃表)原理详解与实现

    ref : https://dsqiu.iteye.com/blog/1705530   本文内容框架: §1 Skip List 介绍 §2 Skip List 定义以及构造步骤   §3 Skip ...