liuzl121 写道
在工作中很少使用java的多线程,以前一直以为java多线程很难,不是很理解其工作原理,前几天有时间上网学习了下java的多线程,以及线程池的一些知识,按照网上的例子也修改了下,弄了个线程池,这期间还是学到了很多知识的。
首先说下java实现多线程的两种方式,这个很多人应该都知道,简单的说说,一种是实现Runnable接口,另一种是继承Thread类,两个方法各有各的好处吧,实现Runnable接口的话可以做到多个线程之间的资源共享,同时java是单继承的,如果继承了Thread类就不能在继承其他类了,而实现了Runnable接口的话就可以同时继承其他类。而继承Thread类的优点呢是获取当前的线程信息比较简单,而我觉得继承Runnable接口的也不复杂,所有个人比较青睐用实现Runnable接口的这种方式。
再说下线程池的原理,线程池会让我联想起数据库连接池,数据库连接池的作用就是给你个数据库连接,然后我们用这个连接去执行操作,而不论我们执行什么操作,数据库连接都不会与我们的逻辑耦合,像一个工具类一样;同样线程池也要满足这样的要求,与我们的逻辑解耦。实现解耦的话就要用到‘多态’或‘接口回调’,就是通过调用父类或接口中的方法从而间接地调用我们所要执行的代码。
以上这些是思路吧,说说原理,不知道有没有人跟我一样,原来我一直以为线程池的话就是多个线程,某一个线程跑完了我们的逻辑方法就结束了,那既然结束了再怎么去接着执行别的任务,所以感觉多线程很难;其实多线程多线程就是通过线程调用线程实现的;打个比方来说就像“摆渡”,河的一岸有很多的人,他们想过河,过河的过程做什么就是他们自己的逻辑,只要他符合我的要求我就送你过河(线程池的要求就是实现Runnable或继承Thread类),然后我开了几条船去送人,只要河的这一岸有满足的人,我就送你过河。这个例子中河一岸的人就是我们要执行的任务,是一个集合,船就是线程池中的线程,由我们自己控制,过河这个动作就是要执行的逻辑,我们只负责把船调给你,怎么划桨怎么过河就是程序自己的逻辑了。
可能说了这么多还是不是很清楚(原谅我的表达能力!),看一个生产者和消费者的例子,生产者不停的生产产品,生产后放入仓库,消费者不停的从仓库里拿产品。我们用线程池将他们实现。
先是创建商品类,商品类中有商品信息:
/**
* 产品类
*/
class Product {
private String id;// 产品id
private String name;// 产品名称
public Product(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "(产品ID:" + id + " 产品名称:" + name + ")";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后创建仓库类,仓库对外开放两个方法,存入和取出,由于是多线程,所以要用synchronized关键字修饰这两个方法。
/**
*仓库
*/
class Storage {
// 仓库容量为10
private Product[] products = new Product[10];
private int top = 0;
// 生产者往仓库中放入产品
public synchronized void push(Product product) {
while (top == products.length) {
try {
System.out.println("仓库满了");
wait();//仓库已满,等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//把产品放入仓库
products[top++] = product;
System.out.println(Thread.currentThread().getName() + " 生产了产品"
+ product);
notifyAll();//唤醒等待线程
}
// 消费者从仓库中取出产品
public synchronized Product pop() {
while (top == 0) {
try {
System.out.println("仓库空了");
wait();//仓库空,等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//从仓库中取产品
--top;
Product p = new Product(products[top].getId(), products[top].getName());
products[top] = null;
System.out.println(Thread.currentThread().getName() + " 消费了产品" + p);
notifyAll();//唤醒等待线程
return p;
}
}
然后生产者消费者的实例,实现Runnable接口,run方法中分别调用入库和出库的方法。
/**
* 消费者
*/
class Consumer implements Runnable {
private Storage storage;
public Consumer(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
storage.pop();
}
}
/**
* 生产者
*/
class Producer implements Runnable {
private Storage storage;
public Producer(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
Product product = new Product("090505105", "电话");
storage.push(product);
}
}
之后就是关键的线程池的实现,线程池首先是一个容器,放着执行的线程和待执行的任务,线程池也只有一个所以用单例的设计模式。对外公开存放作业的方法和设置最大线程数的方法,当外界有作业放进来的时候就执行,执行完了之后销毁。上代码。
package thread;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import org.apache.log4j.Logger;
/**
* 线程池
* 创建线程池,销毁线程池,添加新任务
*
* @author obullxl
*/
public final class ThreadPool {
/* 单例 */
private static ThreadPool instance = ThreadPool.getInstance();
/* 默认池中线程数 */
private static int worker_num = 5;
/* 等待任务队列 */
private static List<Runnable> taskQueue = Collections
.synchronizedList(new LinkedList<Runnable>());
/* 池中的所有线程 */
public PoolWorker[] workers;
/**
* 构造器
* */
private ThreadPool() {
workers = new PoolWorker[5];
for (int i = 0; i < workers.length; i++) {
workers[i] = new PoolWorker(i);
}
}
private ThreadPool(int pool_worker_num) {
worker_num = pool_worker_num;
workers = new PoolWorker[worker_num];
for (int i = 0; i < workers.length; i++) {
workers[i] = new PoolWorker(i);
}
}
/**
* 修改最大线程数
* */
public void setMaxThreadNum(int max){
worker_num = max;
}
/**
* 单例返回本对象
* */
public static synchronized ThreadPool getInstance() {
if (instance == null)
return new ThreadPool();
return instance;
}
/**
* 增加新的任务
* @param newTask
*/
public void addTask(Runnable newTask) {
synchronized (taskQueue) {
taskQueue.add(newTask);
/* 开始执行 */
this.startWork();
}
}
/**
* 批量增加新任务
* @param taskes
*/
public void batchAddTask(List<Runnable> taskes) {
if (taskes == null || taskes.size() == 0) {
return;
}
synchronized (taskQueue) {
for (int i = 0; i < taskes.size(); i++) {
if (taskes.get(i) == null) {
continue;
}
taskQueue.add(taskes.get(i));
}
/* 唤醒队列, 开始执行 */
// taskQueue.notifyAll();
}
for (int i = 0; i < taskes.size(); i++) {
if (taskes.get(i) == null) {
continue;
}
}
/* 开始执行 */
this.startWork();
}
/**
* 执行作业
* */
private void startWork(){
for (int i=0;i<workers.length;i++){
if(!workers[i].isRunning){
workers[i].start();
}
}
}
/**
* 销毁线程池
*/
public synchronized void destroy() {
for (int i = 0; i < worker_num; i++) {
workers[i].stopWorker();
workers[i] = null;
}
taskQueue.clear();
}
/**
* 池中工作线程
*
* @author obullxl
*/
private class PoolWorker extends Thread {
private int index = -1;
/* 该工作线程是否有效 */
private boolean isRunning = false;
/* 该工作线程是否可以执行新任务 */
private boolean isWaiting = true;
public PoolWorker(int index) {
this.index = index;
}
public void stopWorker() {
this.isRunning = false;
}
public boolean isWaiting() {
return this.isWaiting;
}
/**
* 循环执行任务
* 这也许是线程池的关键所在
*/
public void run() {
isRunning = true;
while (isRunning) {
Runnable r = null;
synchronized (taskQueue) {
if (taskQueue.isEmpty()) {
stopWorker(); // (1) 没有作业就不执行
// try { (2) 没有作业就等待,知道有作业加进来
// /* 任务队列为空,则等待有新任务加入从而被唤醒 */
// taskQueue.wait(20);
// } catch (InterruptedException ie) {
// logger.error(ie);
// }
}else{
/* 取出任务执行 */
r = taskQueue.remove(0);
}
}
if (r != null) {
r.run();
}
isWaiting = true;
r = null;
}
}
}
}
然后写一个main方法模拟调用
public class ProducersAndConsumers {
public static void main(String[] args) {
ThreadPool pool = ThreadPool.getInstance();
Storage storage = new Storage();
List<Runnable> conTasks = new ArrayList<Runnable>();
for(int i=0;i<100;i++){
Consumer con = new Consumer(storage);
Producer pro = new Producer(storage);
conTasks.add(con);
conTasks.add(pro);
}
pool.batchAddTask(conTasks);
}
}
将线程池中待执行任务容器中放入任务,然后线程池开启5个线程,去调用我们待执行的任务,从而实现了同一时间内只有5个线程在工作。
演示结果:
仓库空了
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-0 消费了产品(产品ID:090505105 产品名称:电话)
仓库空了
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-0 消费了产品(产品ID:090505105 产品名称:电话)
仓库空了
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
Thread-0 消费了产品(产品ID:090505105 产品名称:电话)
Thread-3 消费了产品(产品ID:090505105 产品名称:电话)
Thread-1 生产了产品(产品ID:090505105 产品名称:电话)
Thread-4 消费了产品(产品ID:090505105 产品名称:电话)
其实这里我们还可在封装下,就是taskQueue继承换成一个实习多线程的通用业务类Task,然后Task类中封装通用的字段和方法。这里就不写了,可以看下这篇文章。
http://blog.csdn.net/touch_2011/article/details/6914468
分享到:
相关推荐
理解Java线程池的原理和实现,可以帮助我们更有效地管理并发任务,提升系统性能,同时避免资源浪费和线程安全问题。在实际开发中,合理配置线程池参数,结合业务场景选择合适的线程池类型,是优化系统性能的关键步骤...
Java线程池是一种高效利用系统资源、管理并发执行任务的机制。...总的来说,理解Java线程池的工作原理和实现对于优化并发应用程序至关重要,它可以帮助我们更好地控制系统的并发度,提高系统的响应速度和资源利用率。
### JAVA线程池原理及几种线程池类型的详细介绍 #### 一、线程池的引入背景及重要性 在现代软件开发中,特别是在基于Java的应用程序设计中,线程池技术已经成为提高系统性能和资源利用率的关键手段之一。线程池...
Java线程池是一种高效管理线程的工具,它允许开发者预先创建一组线程,并复用它们来处理任务,从而降低了创建和销毁线程的开销。线程池的使用尤其适用于处理大量短小任务的场景,例如Web服务器、数据库服务器等。在...
Java线程池由`java.util.concurrent`包中的`ExecutorService`接口和其子类实现。其中,最常用的是`ThreadPoolExecutor`类,它提供了丰富的参数用于定制线程池的行为。线程池的核心组件包括: - **核心线程数...
### 自定义实现Java线程池 #### 一、概述 在深入探讨自定义Java线程池之前,我们先简要回顾一下线程池的基本概念及其重要性。线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动...
Java线程池是一种高效管理并发任务的机制,它允许开发者预先配置一定数量的线程,以便在处理多个并发任务时能有效地复用这些线程,从而避免了频繁创建和销毁线程带来的开销。在Java中,`java.util.concurrent`包下的...
Java线程池是一种高效管理线程的机制,它允许开发者预先设定线程的数量,并通过池化的方式重用已创建的线程,以提高系统性能,减少线程的创建和销毁开销。线程池在Java中是通过`java.util.concurrent`包下的`...
Java线程池是一种高效管理并发任务执行的机制,它通过预先创建并维护一定数量的线程,从而避免了频繁地创建和销毁线程所带来的性能开销。在Java中,线程池的实现主要依赖于`java.util.concurrent`包中的`...
总结来说,自定义Java线程池的实现涉及到线程的管理和调度,包括核心线程的维护、任务队列的使用以及拒绝策略的设定。理解这些机制有助于我们更好地优化并发程序,提高系统效率。然而,在实际开发中,通常推荐使用...
本文所提及的基于Java线程池技术的数据爬虫设计与实现,不仅涉及到了数据爬虫的原理和架构,还包括了多线程编程的知识点,以及线程池技术在数据爬虫中的具体应用。 首先,数据爬虫的基本原理是模拟用户的点击行为,...
Java线程池是一种高效管理线程资源的工具,它通过维护一组可重用的线程来减少创建和销毁线程的开销。在Java中,`java.util.concurrent`包提供了`ExecutorService`接口和它的实现类,如`ThreadPoolExecutor`,来支持...
标题中的“Java实现的...总的来说,这个主题涵盖了Java并发编程中的核心概念,线程池和消息队列的实现原理及应用场景,以及可能用到的相关开发工具。理解并熟练运用这些知识对于提升Java应用的性能和稳定性至关重要。
资料:Java线程池实现原理及其在美团业务中的实践
Java线程池是一种高效管理线程资源的技术,它允许开发者创建一组可重用的工作线程,从而避免频繁地创建和销毁线程带来的性能开销。线程池在Java中主要通过`java.util.concurrent`包中的`ExecutorService`接口及其...
JAVA线程池原理实例详解 JAVA线程池是java中的一种高效的线程管理机制,它可以控制线程的创建、销毁和复用,从而提高系统的性能和可靠性。下面我们将详细介绍JAVA线程池的原理、创建、使用方法及相关注意事项。 一...
Java线程池是Java并发编程中的重要组成部分,它允许开发者高效地管理多个并发执行的线程,有效地控制系统的资源消耗,提高系统性能和稳定性。在Java中,`java.util.concurrent`包提供了`ExecutorService`接口及其...
### Java线程池详解 #### 引言 在现代计算机科学中,线程作为轻量级的进程,已经成为操作系统和应用程序提高并发性、优化资源...理解和掌握Java线程池的原理和使用方法,对于开发高性能、高可靠的Java应用至关重要。
Java线程池是一种高效管理线程的技术,它允许...总之,Java线程池是通过预先创建一组线程和使用工作队列来提高并发性能和资源管理的有效工具。正确配置和使用线程池,有助于优化服务器应用程序的响应时间和资源利用率。