分享一个朋友的人工智能教程。比较通俗易懂,风趣幽默,感兴趣的朋友可以去看看。
简介:
线程相对于进程属于轻量级,虽然线程是轻量级的,合理的使用线程池的好处有
- 降低资源的消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度。当任务到达时,任务可以不需要等到线程池创建就能立即执行
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配,调优和监控。但是要做到合理利用线程池,必须对其实现原理了如指掌。
一:常用类
实际工作中很少显式的去创建线程,因为如果显式创建线程当并发很大时会使得内存耗尽(每个线程都需要分配内存空间),实际开发中一般使用线程池来控制线程的最大个数以及合理重复的利用线程
Exception in thread “main” java.lang.OutOfMemoryError: unable to create new native thread
public static void main(String[] args) {
// 短时间内创建大量线程会使内存耗尽
for (int i = 0; i < 3000; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
Executors
合理配置:
CPU密集(计算):线程数和CPU核数相等
IO密集(读写): 2倍的CPU核数
public class Executors {
// 线程工厂
public static ThreadFactory defaultThreadFactory();
// 创建固定数量的线程池,如果没有任务,那么线程一直等待, n一般与cpu个数一致(Runtime.getRuntime().availableProcessors())
// 最为常用
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
// 创建一个可缓存的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 创建只有一个线程的线程池,所有提交的线程是顺序执行的
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
// 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
}
public interface ExecutorService extends Executor {
// 提交可运行任务供执行,并返回表示该任务的未来。未来的get方法将在成功完成后返回
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);
// 停止线程池接收新的线程,并且执行完已经接收的线程,执行完毕后结束掉线程池(如果不结束掉可以看到main方法会一直运行不会结束,生成上一般都是保持一直运行不需要关闭的)
void shutdown();
}
public interface ThreadFactory {
// 将Runnable包装成Thread
Thread newThread(Runnable r);
}
Callable 的 call()方法只能通过 ExecutorService 的 submit(Callable task) 方法来执行,并且返回一个 Future,是表示任务等待完成的 Future。
当将一个 Callable 的对象传递给 ExecutorService 的 submit 方法,则该 call 方法自动在一个线程上执行,并且会返回执行结果 Future 对象。同样,将 Runnable 的对象传递给 ExecutorService 的 submit 方法,则该 run 方法自动在一个线程上执行,并且会返回执行结果 Future 对象,但是在该 Future 对象上调用 get 方法,将返回 null。
submit首先选择空闲线程来执行任务,如果没有,才会创建新的线程来执行任务
ExecutorService 的 shutdown()方法来平滑地关闭 ExecutorService,调用该方法后,将导致 ExecutorService 停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭 ExecutorService。因此我们一般用该接口来实现和管理多线程。
ExecutorService 的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了 shutdown()方法时,便进入关闭状态,此时意味着 ExecutorService 不再接受新的任务,但它还在执行已经提交了的任务,当素有已经提交了的任务执行完后,便到达终止状态。如果不调用 shutdown()方法,ExecutorService 会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。
public interface Executor {
void execute(Runnable command);
}
二:Runnable示例
ThreadFactory
public static void main(String[] args) {
ThreadFactory threadFactory = Executors.defaultThreadFactory();
Thread thread = threadFactory.newThread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t i=" + i);
}
});
thread.start();
}
newCachedThreadPool
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
// 缓存线程池创建的个数是去不确定的,根据每个线程的总执行时间确定的,当一个线程执行完后会有60秒的存活时间
// 如果有新的任务需要执行就使用刚才执行完空闲的线程执行,如果没有空闲的线程则新启动一个线程,最多启动Integer.MAX_VALUE个线程
for (int i = 0; i < 500; i++) {
int finalI = i;
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t " + "i=" + finalI);
}
});
}
}
这里开启了500个任务,这里测试的缓冲池中有0~48共49个线程,说明其它任务共用了线程池中的线程
newFixedThreadPool
public class ThreadPoolTest {
public static void main(String[] args) {
// 固定线程池,线程池中的个数是固定的值
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
});
executorService.execute(thread);
}
executorService.shutdown();
}
}
我的机器Runtime.getRuntime().availableProcessors()=4,所以有4个线程
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 10; i++) {
int finalI = i;
// 不会立即执行,5秒钟后执行
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tloop i=" + i);
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t " + "i=" + finalI);
}
}, 5, TimeUnit.SECONDS);
}
}
public static void main(String[] args) {
// 所有任务只使用一个线程在执行
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
for (int i = 0; i < 10; i++) {
int finalI = i;
scheduledExecutorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t " + "i=" + finalI);
}
});
}
scheduledExecutorService.shutdown();
}
三:Callable示例
public class ThreadPoolTest {
public static void main(String[] args) {
List<Future<String>> futureList = new ArrayList<>();
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
Future<String> future = executorService.submit(new MyCallable(i));
futureList.add(future);
}
futureList.forEach(future -> {
while (!future.isDone()) { }
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
// 执行以前提交的任务,但不接受新任务
executorService.shutdown();
}
});
}
}
class MyCallable implements Callable<String> {
private int i;
public MyCallable(int i){
this.i = i;
}
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "\t" + "i=" + i;
}
}
四:自定义线程池
前面四种创建线程池的方式很多使用的是ThreadPoolExecutor来创建的,我们也可以使用ThreadPoolExecutor自定义自己的线程池
ThreadPoolExecutor
public class ThreadPoolExecutor extends AbstractExecutorService {
/*
*
* @param corePoolSize 核心线程池: 线程池中所保存的核心线程数,包括空闲线程
* @param maximumPoolSize 最大线程池 池中允许的最大线程数。
* @param keepAliveTime 线程池中的空闲线程所能持续的最长时间
* @param unit keepAliveTime对应的单位
* @param workQueue 任务执行前保存任务的队列,仅保存由 execute方法提交的Runnable任务
*
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory);
/*
* 当创建一个线程池的时候,该线程池会包含两个线程,一个是核心线程池,一个是最大线程池。
* 当提交一个任务的时候,如果核心线程池没有满的情况下就在核心线程池中执行
* 如果核心线程池已经满了,就把该线程放到队列中,如果核心线程池有空闲的线程就从队列中取出来执行
* 当队列满的情况下(每种队列都会设置最大的容量),就把线程放到最大线程池中去执行
* 当最大线程池也已经满的情况下就走拒绝策略
*/
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
}
public static void main(String[] args) {
// 等待队列
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(20);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MICROSECONDS, blockingQueue);
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t running...");
try { Thread.sleep(500); } catch (InterruptedException e) { }
});
}
threadPool.shutdown();
}
分享一个朋友的人工智能教程。比较通俗易懂,风趣幽默,感兴趣的朋友可以去看看。