`
oham_一1一
  • 浏览: 49808 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java线程基础——线程池使用示例

阅读更多

引言

合理利用线程池能够带来三个好处:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

 

本篇参考:http://www.infoq.com/cn/articles/java-threadPool

                  http://www.cjsdn.net/Doc/JDK60/

 

关于线程池的配置参数

Java中创建线程池创建如下:

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

 ThreadPoolExecutor的构造函数有多个,通过构造函数的参数配置线程池,以下是各个参数的说明:

corePoolSize:线程池的基本大小,maximumPoolSize:线程池最大大小,即线程池允许创建的最大线程数,线程池会根据corePoolSize(调用getCorePoolSize())和maximumPoolSize(调用getMaximumPoolSize())去自动调整池中的线程数量(调用getPoolSize())。当池中的线程数少于corePoolSize的时候,会创建新的线程放到任务队列处理请求,不管队列中其他的线程是否空闲;当线程数大于coolPoolSize而小于maximumPoolSize的时候,如果池中任务队列为空,则创建线程放入任务队列。当设置maximumPoolSize为Integer.MAX_VALUE,则这个线程池为无界线程池,然后池中的线程数量是任意的。如果任务队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务,该线程等待进入任务队列。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。(鄙人估计corePoolSize确定了任务队列的大小)

 

workQueue:任务队列,用于转移和阻塞提交了的任务,即任务队列是运行线程的,任务队列根据corePoolSize和maximumPoolSize工作:

   1.当正在运行的线程小于coolPoolSize,线程池会创建新的线程。

   2.当大于coolPoolSize而任务队列未满,则从队列里拿一个空闲的线程去接任务。

   3.当大于coolPoolSize而任务队列满了(即队列中没有空闲的线程),并且小于maximumPoolSize,会创建新的线程接任务,该线程等待进入任务队列。

   4.当大于maximumPoolSize,该任务会根据handler(RejectedExecutionHandler,饱和策略)处理。

参考图:

 任务队列又有以下集中策略模式:

    1.直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。    

    2.无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

  3. 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

 

handler:RejectedExecutionHandler(饱和策略),当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。

   1.在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException

   2.在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

   3.在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。

   4.在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。

定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
unit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。如果没有另外说明,则在同一个 ThreadGroup 中一律使用 Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread 返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。
 
钩子 (hook) 方法 
此类提供 protected 可重写的 beforeExecute(java.lang.Thread, java.lang.Runnable) 和 afterExecute(java.lang.Runnable, java.lang.Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化 ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法 terminated() 来执行 Executor 完全终止后需要完成的所有特殊处理。   如果钩子 (hook) 或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。队列维护   方法 getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(java.lang.Runnable) 和 purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。
 
线程池的关闭
我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。
 
总结
举个不太优雅的例子,将线程池比喻成一个窑子,每个任务都是嫖客,线程就是接待客人的妹子,一个任务一条线程(不搞多P),corePoolSize是坐台的妹子数,maximumPoolSize是妹子的总数,任务队列是排着正在ooxx的以及准备ooxx的其大小为一次最多只允许有多少对在ooxx,keepAliveTime是每个妹子最大工作时长,做完若没超过时长就是闲着的,从闲着的坐台妹子选出再接另外的,若没闲着的坐台妹子,再叫人数过来补充,但不能超过妹子总数,超过了的话按经营策略(RejectedExecutionHandler——策略处理,所以可以想象,窑子老板就想多赚,坐台的妹子只要还行,接着来,不会轻易给你个新鲜的因为有任务队列大小限制(房间有限,允许ooxx的对数不能太多),若队列满了,生意好,客人只能携着妹子等了。。。。大概意思是如此,给个图自己脑补一下:

 
 
以下是示例代码:
ThreadPoolGenerator.java,封装了ThreadPoolExecutor的创建:
package testThreadPool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolGenerator {

	private volatile static ThreadPoolExecutor threadPoolExecutor;
	
	//写了自己的一个Executor,主要是用于线程池执行完所有任务所需的时间,重写了terminated方法
	public static class MyThreadPoolExecutor extends ThreadPoolExecutor {

		long sTime = 0L;
		long eTime = 0L;
		
		public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
				long keepAliveTime, TimeUnit unit,
				BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
				RejectedExecutionHandler handler) {
			super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
					threadFactory, handler);
		}
		
		@Override
		protected void terminated() {
			super.terminated();
			if(sTime != 0l) {
				eTime = System.currentTimeMillis();
				System.out.println("executor completed tasks take: " + (eTime - sTime) + " ms");
			}
			
		}
		
		//记录Executor开始执行的那刻的时间
		public void startLogTime() {
			sTime = System.currentTimeMillis();
		}
	}
	
	public static ThreadPoolExecutor getThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			Long keepAliveTime, int quequeSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
		
		if(threadPoolExecutor == null) {
			synchronized (ThreadPoolGenerator.class) {
				if(threadPoolExecutor == null) {
					threadFactory = threadFactory == null ? Executors.defaultThreadFactory() : threadFactory;
					handler = handler == null ? new ThreadPoolExecutor.AbortPolicy() : handler;
					
					threadPoolExecutor = new MyThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
							TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(quequeSize), threadFactory, handler);
				}
			}
		}
		
		return threadPoolExecutor;
	}
}
TestTask.java, 一个测试任务类:
package testThreadPool;

public class TestTask implements Runnable{

	private String name;
	
	public TestTask(String name) {
		this.name = name;
	}
	
	
	//模拟任务,此处两个for 循环,若循环次数不大,那么有线程执行跟没线程执行效果差不多的
	public void performTask() {
		for(int i=0; i<100000000; i++){
			for(int j=0; j<1000000; j++) {
				j=i+j;
			}
		}
		
		System.out.println(name + " completed.");
	}

	//通过线程执行模拟任务
	@Override
	public void run() {
		this.performTask();
		
	}
}
 
TestCase.java,测试类:
package testThreadPool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

import testThreadPool.ThreadPoolGenerator.MyThreadPoolExecutor;


public class TestCase {

	
	public static void main(String[] args) {
		
		List<TestTask> tasks = new ArrayList<TestTask>();
		for(int i=1; i<=5; i++) 
			tasks.add(new TestTask("Task " + i));
		
		
		TestCase c = new TestCase();
		
		System.out.println("no thread case:");
		
		long sTime = System.currentTimeMillis();
		
		c.noThreadProcess(tasks);
		
		long eTime = System.currentTimeMillis();
		long tTime = eTime - sTime;
		System.out.println("process take :" + tTime + "ms");
		
		System.out.println("=========================");

		
		System.out.println();
		
		
		System.out.println("with thread case:");
		
		ThreadPoolExecutor executor = ThreadPoolGenerator.getThreadPoolExecutor(3, 5, 5000L, 3, null, null);
		
		c.withThreadProcess(executor, tasks);
		
		eTime = System.currentTimeMillis();
		
		System.out.println("=========================");
		
	}
	
	
	
	public void withThreadProcess(ThreadPoolExecutor executor, List<TestTask> list) {
		
		//此举用来启动记录执行耗时
		((MyThreadPoolExecutor)executor).startLogTime();
		
		for(TestTask task : list) {
			executor.execute(task);
		}
		executor.shutdown();
		
	}
	
	public void noThreadProcess(List<TestTask> list) {
		
		for(TestTask task : list) {
			task.performTask();
		}
		
	}

}
 测试结果:
no thread case:
Task 1 completed.
Task 2 completed.
Task 3 completed.
Task 4 completed.
Task 5 completed.
process take :2802ms
=========================

with thread case:
=========================
Task 3 completed.
Task 2 completed.
Task 1 completed.
Task 4 completed.
Task 5 completed.
executor completed tasks take: 1224 ms
 
 
 线程池另外一种用法,用线程池执行任务,完成后的取得执行结果:
TestMessageTask.java,那个badTask如果为true,表示为坏任务,模拟任务执行出错:
package testThreadPool;

import java.util.concurrent.Callable;

public class TestMessageTask implements Callable<String> {

	private String name;
	private boolean badTask;
	
	public TestMessageTask(String name, boolean badTask) {
		this.name = name;
		this.badTask = badTask;
	}
	
	@Override
	public String call() throws Exception {
		for(int i=0; i<100000000; i++){
			for(int j=0; j<1000000; j++) {
				j=i+j;
			}
		}
		
		//用了一个boolean,模拟执行出错的情形
		if(badTask) {
			throw new RuntimeException(name + " is bad task!!");
		}
		return name + " completed.";
	}

}
 
MessageThreadPoolExecutor.java:
package testThreadPool;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MessageThreadPoolExecutor extends ThreadPoolExecutor{

	
	//把父类的构造函数全弄出来算了。。。
	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
	}

	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
				threadFactory);
	}

	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
	}

	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
			RejectedExecutionHandler handler) {
		
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
				threadFactory, handler);
	}
	
	
	//定义了一个方法执行任务集合,用了泛型,表示结果类型由Callable类型的任务对象决定
	public <T> Map<String, T> performTasks(Map<String, Callable<T>> taskMap) throws InterruptedException{
		
		if(taskMap == null || taskMap.isEmpty()) 
			throw new NullPointerException();
		
		Map<String, Future<T>> futureMap = new HashMap<String, Future<T>>();
		Map<String, T> messageMap = new HashMap<String, T>();
		
		boolean done = false;
		
		try {
			
			for(String key : taskMap.keySet()) {
				/**
				 * 这里用了两种方式执行任务:execute与submit,建议翻翻API文档,
				 * 关于Future的get方法,没弄懂。。。
				 */
				//execute 方式
				/*RunnableFuture<T> futureTask = new FutureTask<T>(taskMap.get(key));
				execute(futureTask);
				futureMap.put(key, futureTask);*/
				
				
				//submit 方式
				futureMap.put(key, submit(taskMap.get(key)));
				
			}
			
			/**
			 *  再次遍历任务,逐个调用get方法,get方法会阻塞住直到任务完成,
			 *  get方法返回一个结果,根据结果判断任务执行成功与否,这也是我没有看懂
			 *  API的地方,那个submit方法明明说返回的Future对象如果成功它的get方法
			 *  返回null,但messageMap中的value是有的,不为null。。。 
			 */
			for(String key : futureMap.keySet()) {
				Future<T> f = futureMap.get(key);
				try {
					T result = f.get();
					messageMap.put(key, result);
				} catch (ExecutionException e) {
					System.out.println(e.getMessage());
				}
			}
			
			done = true;
			return messageMap;
		}finally {
			//若上面出了异常没done,没做完的任务直接cancel
			if(!done) {
				for(String key : futureMap.keySet()) {
					futureMap.get(key).cancel(true);
				}
			}
			
			this.shutdown();
		}
		
	}
	

}
 
测试代码,MsgTestCase.java:
package testThreadPool;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

public class MsgTestCase {

	public static void main(String[] args) {
		
		Map<String, Callable<String>>taskMap = new HashMap<String, Callable<String>>();
		for(int i=1; i<=5; i++) {
			
			//随机产生坏任务
			int r = (int)(Math.random()*5 + 1);
			boolean badTask = (i==r);
			
			taskMap.put("Task: " + i ,new TestMessageTask("Task: " + i, badTask));
		}
		
		
		MessageThreadPoolExecutor executor = new MessageThreadPoolExecutor(3, 5, 3000L, 
					TimeUnit.MILLISECONDS, 
					new ArrayBlockingQueue<Runnable>(2));
		
		try {
			Map<String, String> resultMap = executor.performTasks(taskMap);
			
			for(String key : resultMap.keySet()) {
				System.out.println(resultMap.get(key));
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
		
	}

}
 测试结果:
java.lang.RuntimeException: Task: 5 is bad task!!
java.lang.RuntimeException: Task: 2 is bad task!!
Task: 3 completed.
Task: 4 completed.
Task: 1 completed.
 
 
  • 大小: 64.4 KB
  • 大小: 13.2 KB
分享到:
评论

相关推荐

    Java SE实践教程 pdf格式电子书 下载(四) 更新

    4.1.1 流——Java I/O的基础 72 4.1.2 Java I/O库 72 4.2 练习 74 4.2.1 数据传送的通道 74 4.2.2 管道的一端 76 4.2.3 文件处理 78 4.2.4 基于对象的读写 80 4.2.5 NIO 85 4.3 小结 89 第5章 如何走得更稳...

    Java网络编程(第三版)中文版.part09.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java SE实践教程 源代码 下载

    4.1.1 流——Java I/O的基础 72 4.1.2 Java I/O库 72 4.2 练习 74 4.2.1 数据传送的通道 74 4.2.2 管道的一端 76 4.2.3 文件处理 78 4.2.4 基于对象的读写 80 4.2.5 NIO 85 4.3 小结 89 第5章 如何走得更稳...

    Java SE实践教程 pdf格式电子书 下载(一) 更新

    4.1.1 流——Java I/O的基础 72 4.1.2 Java I/O库 72 4.2 练习 74 4.2.1 数据传送的通道 74 4.2.2 管道的一端 76 4.2.3 文件处理 78 4.2.4 基于对象的读写 80 4.2.5 NIO 85 4.3 小结 89 第5章 如何走得更稳...

    Java网络编程(第三版)中文版.part11.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part06.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part07.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)高清中文版.part01.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part01.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part03.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part04.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part02.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part05.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part13.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part10.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part12.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    Java网络编程(第三版)中文版.part08.rar

    ——Bruce Eckel,《Thinking in Java》的作者 内容简介回到顶部↑《Java网络编程》第三版会为你介绍Java网络API的最新特性。本书讨论了JDK 1.4和1.5(现在已命名为J2SE 5)中所做的所有修改和增补。本书内容全面,...

    javaSE代码实例

    第16章 多线程——Java中的并发协作 343 16.1 线程的基本知识 343 16.1.1 多线程编程的意义 343 16.1.2 定义自己的线程 344 16.1.3 创建线程对象 345 16.1.4 启动线程 347 16.1.5 同时使用多个线程 ...

    javaforkjoin源码-filespilt-demo:初始化项目

    单线程读多线程写方案,该方案使用了两种不同的线程池实现:ThreadPoolExcutor和ForkJoinPool,分别对应NORMAL和FORKJOIN两种执行模式; 生产者-消费者模式下的多线程读/写方案,对应PRODUCERCONSUMER执行模式。 ...

    开涛高可用高并发-亿级流量核心技术

    12.3.1 Java线程池 245 12.3.2 Tomcat线程池配置 248 13 异步并发实战 250 13.1 同步阻塞调用 251 13.2 异步Future 252 13.3 异步Callback 253 13.4 异步编排CompletableFuture 254 13.5 异步Web服务实现 257 13.6 ...

Global site tag (gtag.js) - Google Analytics