目录

Executors创建线程池默认方法

自定义线程池


Executors创建线程池默认方法

newFixedThreadPool()方法,该方法返回一个固定数量的线程池,该方法的线程数始终不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中等待有空闲的线程去执行。

newSingleThreadExecutor()方法,创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列。

newCachedThreadPool()方法,返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若有空闲的线程则直接执行任务,若无空闲则创建线程,若无任务则不创建线程。并且每一个空闲线程会在60秒后自动回收。

newScheduledThreadPool()该方法返回一个ScheduledExecutorService对象,但该线程池可以指定线程的数量,可以定时。

前三种线程池添加线程:
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(new Thread());
//submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值
Future f1 = pool.submit(new Thread());
f1.get()//如果为空,则线程执行完毕了,否则会一直阻塞在这里。
//ScheduledThreadPool相当于定时任务
class Temp extends Thread {public void run() {System.out.println("run");}
}
public class ScheduledJob {public static void main(String args[]) throws Exception {Temp command = new Temp();ScheduledExecutorService scheduler =  Executors.newScheduledThreadPool(1);ScheduledFuture<?> scheduleTask =  scheduler.scheduleWithFixedDelay(command, 5, 1,  TimeUnit.SECONDS);//5秒初始化之后执行一次,以后每1秒执行一次}
}

其实底层都是new了ThreadPoolExecutor。

若Executors工厂类无法满足我们的需求,可以自己去创建自定义的线程池,其实Executors工厂类里面的创建线程方法其内部实现均是用了ThreadPoolExecutor这个类,这个类可以自定义线程。构造方法如下:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {}
* corePoolSize: 线程池核心线程数(线程池初始化存在的线程)
* maximumPoolSize:线程池最大数
* keepAliveTime: 空闲线程存活时间
* unit: 时间单位
* workQueue: 线程池所使用的缓冲队列
* threadFactory:线程池创建线程使用的工厂
* handler: 线程池对拒绝任务的处理策略

自定义线程池

这个构造方法对于队列是什么类型的比较关键。

使用有界队列时:若有新的任务需要执行,如果线程池实际线程小于corePoolSize,则优先创建线程,若大于corePoolSize,则会将任务加入队列,若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程去执行新任务,若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。

使用无界队列时:LinkedBlockingQueue。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加。若后续仍有新的任务加入,而又没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

JDK拒绝策略

AbortPolicy:直接抛出异常组织系统正常工作。

CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。

DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务。

DiscardPolicy:丢弃无法处理的任务,不给予任何处理。

//如果需要自定义拒绝策略可以实现RejectedExecutionHandler接口。
public class MyTask implements Runnable {private int taskId;private String taskName;public MyTask(int taskId, String taskName){this.taskId = taskId;this.taskName = taskName;}public int getTaskId() {return taskId;}public void setTaskId(int taskId) {this.taskId = taskId;}public String getTaskName() {return taskName;}public void setTaskName(String taskName) {this.taskName = taskName;}@Overridepublic void run() {try {System.out.println("run taskId =" +  this.taskId);Thread.sleep(5*1000);//System.out.println("end taskId =" +  this.taskId);} catch (InterruptedException e) {e.printStackTrace();}          }public String toString(){return Integer.toString(this.taskId);}
}public class UseThreadPoolExecutor1 {public static void main(String[] args) {/*** 在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,* 若大于corePoolSize,则会将任务加入队列,* 若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,* 若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。**/  ThreadPoolExecutor pool = new ThreadPoolExecutor(1,                   //coreSize2,                   //MaxSize60,             //60TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(3)             //指定一种队列 (有界队列)//new LinkedBlockingQueue<Runnable>(), new MyRejected()//, new DiscardOldestPolicy());MyTask mt1 = new MyTask(1, "任务1");MyTask mt2 = new MyTask(2, "任务2");MyTask mt3 = new MyTask(3, "任务3");MyTask mt4 = new MyTask(4, "任务4");MyTask mt5 = new MyTask(5, "任务5");MyTask mt6 = new MyTask(6, "任务6");pool.execute(mt1);pool.execute(mt2);pool.execute(mt3);pool.execute(mt4);pool.execute(mt5);pool.execute(mt6);pool.shutdown();}
}public class MyRejected implements RejectedExecutionHandler{public MyRejected(){}@Overridepublic void rejectedExecution(Runnable r,  ThreadPoolExecutor executor) {System.out.println("自定义处理..");System.out.println("当前被拒绝任务为:" +  r.toString());}
}
public class UseThreadPoolExecutor2 implements Runnable{private static AtomicInteger count = new AtomicInteger(0);@Overridepublic void run() {try {int temp = count.incrementAndGet();System.out.println("任务" + temp);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) throws Exception{//System.out.println(Runtime.getRuntime().availableProcessors());BlockingQueue<Runnable> queue =//new LinkedBlockingQueue<Runnable>();new ArrayBlockingQueue<Runnable>(10);ExecutorService executor  = new ThreadPoolExecutor(5,         //core10,  //max无界队列的这个参数其实没啥作用了120L,      //2fenzhongTimeUnit.SECONDS,queue);for(int i = 0 ; i < 20; i++){executor.execute(new UseThreadPoolExecutor2());}Thread.sleep(1000);System.out.println("queue size:" + queue.size());         //10Thread.sleep(2000);}
}

线程池的方法

向线程池提交任务

execute()

execute()方法用于提交不需要返回值的任务Runnable,所以无法判断任务是否被线程池执行成功。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;import lombok.extern.slf4j.Slf4j;import java.util.concurrent.*;
import java.util.stream.IntStream;@Slf4j
public class ExecuteDemo {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());IntStream.rangeClosed(1, 3).forEach(i -> threadPoolExecutor.execute(() -> log.info("Task {} working...", i))); // 提交任务threadPoolExecutor.shutdown(); // 关闭线程池}
}

运行结果如下:

2020-10-13 14:58:09,500  INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 1 working...
2020-10-13 14:58:09,502  INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 2 working...
2020-10-13 14:58:09,502  INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 3 working...

submit()
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,get()方法也支持带超时时间的阻塞。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;import lombok.extern.slf4j.Slf4j;import java.util.concurrent.*;
import java.util.stream.IntStream;@Slf4j
public class SubmitDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());Future<String> future = threadPoolExecutor.submit(() -> {log.info("Task begin...");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}log.info("Task end.");return "task result";});threadPoolExecutor.shutdown(); // 关闭线程池String result = future.get(); // 阻塞等待获取结果log.info("get result:{}", result);}
}

运行结果如下:

2020-10-13 15:03:22,306  INFO [pool-1-thread-1] (SubmitDemo.java:14) - Task begin...
2020-10-13 15:03:25,315  INFO [pool-1-thread-1] (SubmitDemo.java:20) - Task end.
2020-10-13 15:03:25,316  INFO [main] (SubmitDemo.java:27) - get result:task result

invokeAll()

package com.morris.concurrent.threadpool.threadpoolexecutor.api;import lombok.extern.slf4j.Slf4j;import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;@Slf4j
public class InvokeAllDemo {public static void main(String[] args) throws InterruptedException, ExecutionException {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());List<Callable<String>> tasks = IntStream.rangeClosed(1, 3).boxed().map(i -> (Callable<String>) () -> "task " + i).collect(Collectors.toList());List<Future<String>> futures = threadPoolExecutor.invokeAll(tasks);// 批量提交任务for (Future<String> future : futures) {log.info("result:{}", future.get()); // 阻塞获取结果}threadPoolExecutor.shutdown();}
}

运行结果如下:

2020-10-13 16:09:00,518  INFO [main] (InvokeAllDemo.java:25) - result:task 1
2020-10-13 16:09:00,520  INFO [main] (InvokeAllDemo.java:25) - result:task 2
2020-10-13 16:09:00,520  INFO [main] (InvokeAllDemo.java:25) - result:task 3

invokeAny()

invokeAny批量提交任务,哪个任务先执行完毕,立刻返回此任务执行结果,其它任务取消。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;import lombok.extern.slf4j.Slf4j;import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;@Slf4j
public class InvokeAnyDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());List<Callable<String>> tasks = IntStream.rangeClosed(1, 3).boxed().map(i -> (Callable<String>) () -> {log.info("task{} begin ...", i);TimeUnit.SECONDS.sleep(new Random().nextInt(10));log.info("task{} end .", i);return "task" + i;}).collect(Collectors.toList());String result = threadPoolExecutor.invokeAny(tasks); // 哪个任务执行完就立马返回,其他任务直接中断log.info("result:{}", result); // 阻塞获取结果threadPoolExecutor.shutdown();}
}

运行结果如下:

2020-10-13 16:09:35,121  INFO [pool-1-thread-1] (InvokeAnyDemo.java:21) - task1 begin ...
2020-10-13 16:09:35,121  INFO [pool-1-thread-2] (InvokeAnyDemo.java:21) - task2 begin ...
2020-10-13 16:09:35,121  INFO [pool-1-thread-3] (InvokeAnyDemo.java:21) - task3 begin ...
2020-10-13 16:09:38,125  INFO [pool-1-thread-3] (InvokeAnyDemo.java:23) - task3 end .
2020-10-13 16:09:38,125  INFO [main] (InvokeAnyDemo.java:29) - result:task3

线程池的关闭

shutdown()

shutdown()方法会先将线程池的状态改为SHUTDOWN,然后中断空闲的线程,不会再接收新任务,但已提交任务会执行完。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;import lombok.extern.slf4j.Slf4j;import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.IntStream;/*** 线程池的关闭之shutdown()*/
@Slf4j
public class ShutdownDemo {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());IntStream.rangeClosed(1, 10).forEach(i -> threadPoolExecutor.submit(() -> {log.info("task{} begin ...", i);try {TimeUnit.SECONDS.sleep(new Random().nextInt(3));} catch (InterruptedException e) {e.printStackTrace();}log.info("task{} end .", i);}));threadPoolExecutor.shutdown();threadPoolExecutor.submit(() -> log.info("submit task after shutdown"));}
}

运行结果如下:

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@18769467 rejected from java.util.concurrent.ThreadPoolExecutor@46ee7fe8[Shutting down, pool size = 2, active threads = 2, queued tasks = 8, completed tasks = 0]at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)at com.morris.concurrent.threadpool.threadpoolexecutor.api.ShutdownDemo.main(ShutdownDemo.java:30)
2020-10-13 16:54:42,637  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task1 begin ...
2020-10-13 16:54:42,637  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task2 begin ...
2020-10-13 16:54:43,639  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task2 end .
2020-10-13 16:54:43,639  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task1 end .
2020-10-13 16:54:43,640  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task3 begin ...
2020-10-13 16:54:43,640  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task4 begin ...
2020-10-13 16:54:43,641  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task4 end .
2020-10-13 16:54:43,641  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task5 begin ...
2020-10-13 16:54:44,642  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task5 end .
2020-10-13 16:54:44,642  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task6 begin ...
2020-10-13 16:54:45,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task3 end .
2020-10-13 16:54:45,642  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task7 begin ...
2020-10-13 16:54:46,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task7 end .
2020-10-13 16:54:46,642  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task6 end .
2020-10-13 16:54:46,642  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task8 begin ...
2020-10-13 16:54:46,642  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task9 begin ...
2020-10-13 16:54:47,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task8 end .
2020-10-13 16:54:47,642  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task10 begin ...
2020-10-13 16:54:47,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task10 end .
2020-10-13 16:54:48,643  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task9 end .

shutdownNow()

shutdownNow()会先将会先将线程池的状态改为STOP,不会再接收新任务,然后中断所有的线程,如果执行中的任务没有对中断进行处理,那么这个任务将会继续执行直至完成,如果执行中的任务对中断进行的处理,那么将按中断进行执行,最后方法会返回等待队列中未执行的任务。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;import lombok.extern.slf4j.Slf4j;import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.IntStream;/*** 线程池的关闭之shutdownNow()*/
@Slf4j
public class ShutdownNowDemo {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());IntStream.rangeClosed(1, 10).forEach(i -> threadPoolExecutor.submit(() -> {log.info("task{} begin ...", i);try {TimeUnit.SECONDS.sleep(new Random().nextInt(3));} catch (InterruptedException e) {e.printStackTrace();}log.info("task{} end .", i);}));List<Runnable> runnables = threadPoolExecutor.shutdownNow();log.info("tasks size:{}", runnables.size());}
}

运行结果如下:

2020-10-13 16:55:08,985  INFO [main] (ShutdownNowDemo.java:30) - tasks size:8
2020-10-13 16:55:08,987  INFO [pool-1-thread-2] (ShutdownNowDemo.java:20) - task2 begin ...
2020-10-13 16:55:08,987  INFO [pool-1-thread-1] (ShutdownNowDemo.java:20) - task1 begin ...
2020-10-13 16:55:08,988  INFO [pool-1-thread-2] (ShutdownNowDemo.java:26) - task2 end .
2020-10-13 16:55:08,989  INFO [pool-1-thread-1] (ShutdownNowDemo.java:26) - task1 end .

awaitTermination()

调用shutdown()后,调用线程并不会等待所有任务运行结束,可以利用awaitTermination()方法设置一个超时时间进行阻塞等待,此方法返回了有两种情况,要么所有的任务执行完成,要么超时时间到了。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;import lombok.extern.slf4j.Slf4j;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*** threadPoolExecutor.awaitTermination()的使用*/
@Slf4j
public class AwaitTerminationDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());threadPoolExecutor.submit(() -> {log.info("task begin ...");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}log.info("task end .");});threadPoolExecutor.shutdown(); // 关闭线程池threadPoolExecutor.awaitTermination(10, TimeUnit.SECONDS);log.info("main thread exit");}
}

运行结果如下:

2020-10-13 17:01:45,397  INFO [pool-1-thread-1] (AwaitTerminationDemo.java:16) - task begin ...
2020-10-13 17:01:48,398  INFO [pool-1-thread-1] (AwaitTerminationDemo.java:22) - task end .
2020-10-13 17:01:48,398  INFO [main] (AwaitTerminationDemo.java:27) - main thread exit

创建线程的工厂ThreadFactory

可以自定义ThreadFactory为线程池中的线程取一个有意义的名字,方便后面快速定位问题。

package com.morris.concurrent.threadpool.threadpoolexecutor.construct;import lombok.extern.slf4j.Slf4j;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;/*** 使用ThreadFactory自定义线程名*/
@Slf4j
public class ThreadFactoryDemo {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(), new ThreadFactory() {private AtomicInteger idx = new AtomicInteger(1);private static final String THREAD_NAME_PREFIX = "mythread-pool-";@Overridepublic Thread newThread(Runnable r) {return new Thread(r, THREAD_NAME_PREFIX + idx.getAndIncrement());}});threadPoolExecutor.submit(()->{log.info("task begin ...");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}log.info("task end .");});threadPoolExecutor.shutdown();}
}

谈谈java的线程池(创建、机制)相关推荐

  1. Java队列——线程池创建的例子

    线程池为线程生命周期开销问题和资源不足问题提供了解决方案.通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上.其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟.这 ...

  2. java 线程池 复用机制,java的线程池框架及线程池的原理

    java 线程池详解 什么是线程池? 提供一组线程资源用来复用线程资源的一个池子 为什么要用线程池? 线程的资源是有限的,当处理一组业务的时候,我们需要不断的创建和销毁线程,大多数情况下,我们需要反复 ...

  3. 【Android 异步操作】线程池 ( 线程池作用 | 线程池种类 | 线程池工作机制 | 线程池任务调度源码解析 )

    文章目录 一.线程池作用 二.线程池种类 三.线程池工作机制 四.线程池任务调度源码解析 一.线程池作用 线程池作用 : ① 避免创建线程 : 避免每次使用线程时 , 都需要 创建线程对象 ; ② 统 ...

  4. 自定义java线程池_我的Java自定义线程池执行器

    自定义java线程池 ThreadPoolExecutor是Java并发api添加的一项功能,可以有效地维护和重用线程,因此我们的程序不必担心创建和销毁线程,也不必关注核心功能. 我创建了一个自定义线 ...

  5. 我的Java自定义线程池执行器

    ThreadPoolExecutor是Java并发api添加的一项功能,可以有效地维护和重用线程,因此我们的程序不必担心创建和销毁线程,而将精力放在核心功能上. 我创建了一个自定义线程池执行程序,以更 ...

  6. java中线程池的几种实现方式

    1.线程池简介:     多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.         假设一个服务器完成一项任务所需时间为:T1 ...

  7. java excutorthread_JAVA 线程池ThreadPoolExcutor原理探究

    概论 线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间 ...

  8. Java六大线程池和四大拒绝策略

    Java六大线程池和四大拒绝策略 学习常见的 6 种线程池,并详细讲解 Java 8 新增的 ForkJoinPool 线程池,6 种常见的线程池如下. FixedThreadPool CachedT ...

  9. [Java高并发系列(5)][详细]Java中线程池(1)--基本概念介绍

    1 Java中线程池概述 1.1 什么是线程池? 在一个应用当中, 我们往往需要多次使用线程, 这意味着我们需要多次创建和销毁线程.那么为什么不提供一个机制或概念来管理这些线程呢? 该创建的时候创建, ...

最新文章

  1. 深度学习(5)感知机(神经元)与神经网络
  2. java学习与总结:线程池
  3. linux通信--信号量
  4. 2.6.21相比2.4.18内核机制变更
  5. 中山市区电信5g覆盖地图_2020中山数字经济发展论坛举行,上线工业互联网平台...
  6. python入门if语句练习_python入门视频:09 if语句_练习.mp4
  7. CentOS 7 搭建 LAMP
  8. 95-36-110-ChannelHandler-ChannelDuplexHandler
  9. CDN的原理技术及使用方法
  10. 电子商务网络购物平台实例运营分析+电子商务概述及阿里巴巴集团模式五大核心内容[连载之电子商务网络营销]...
  11. outlook2016登录163邮箱教程
  12. 微信小程序引入iconfont阿里字体
  13. 黑暗城堡(最短路径树)
  14. DEDE源码分析与学习--index.php文件解读
  15. 使用计算机要遵循哪些规则,中国大学MOOC:\\\在计算机网络的定义中,把众多计算机有机连接起来要遵循规定的约定和规则,称之为( )。\\\;...
  16. WPF DataGrid MVVM 绑定 SelectedCells
  17. 微信公众平台开发——引言
  18. 网站关键词排名优化中常见的问题及解决方法
  19. Now trying to drop the old temporary tablespace, the session hangs.
  20. 数据结构学习笔记 哈希表(一) 哈希表基础与哈希函数

热门文章

  1. unresolved external symbol怎么解决_收藏!用Kubernetes和PKS 1.5解决Windows Server2008的问题...
  2. java 公共组件_【JAVA语言程序设计基础篇】--Swing GUI组件的公共特性
  3. java string转number_Java运算符知识点总结
  4. fanuc机器人试题_黄冈职业技术学院工业机器人技术专业简介
  5. mac 卸载 eclipse_Mac 新手准备工具集合
  6. java calendar类_2020 年,你还在使用 Java 中的 SimpleDateFormat 吗?
  7. 软件著作权 开源框架_开源软件分享-基于.net core 3.1的快速开发框架
  8. 苹果8怎么投屏到电视_创维电视怎么投屏
  9. 计算机视觉领域,计算机视觉
  10. 如何将文件地址转为url_Node.js 源码解析 util.promisify 如何将 Callback 转为 Promise