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线程池的原理与实现.pdf
介绍了java线程池的基本实现原理并由详细的实例
主要介绍了Java线程池FutureTask实现原理详解,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
Java语言游戏项目实战资源包 内容概览: 这次分享为你带来了丰富的Java语言游戏项目实战资源,让你在实践中深入...持续学习与探索:Java语言和游戏开发技术都在不断更新,建议你在实践中持续学习新的技术和工具,不
主要为大家详细介绍了Java concurrency线程池之线程池原理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
2.线程池的运作原理 3.调度线程池的运作原理 4.调度线程池怎么实现FixRate,FixDelay 5.怎么取消的
}关于FutureTask这个类的实现,我在前面的JAVA LOCK代码浅析有讲过其实现原理,主要的思想就是关注任务完成与未完成的状态,任务提交线程get()结
因此,它适合随机查找和遍历,不适合插入和删除。 2.Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步...
主要介绍了java基于线程池和反射机制实现定时任务的方法,以完整实例形式较为详细的分析了Java定时任务的功能原理与实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
主要介绍了深入理解Java编程线程池的实现原理,涉及ThreadPoolExecutor类,线程池实现原理及示例等相关内容,具有一定参考价值,需要的朋友可以了解下。
4、ReentrantLock底层实现和如何使用 5、Condition源码分析 6、ReentrantReadWriteLock底层实现原理 7、并发工具类CountDownLatch 、CyclicBarrier和Semaphore底层实现原理 8、线程池原理和如何使用线程池 9、...
本节主要关注在配置和调整线程池时用的高级选项,讲述了任务执行框架的过程中需 要注意的危险。线程复用原理如下:每一个 Thread 的类都有一个 start 方法。 当调用 start 启动线程时 Java 虚拟机会调 用该类的 run...
本文档对Java中的线程和线程池进行了简单介绍。首先,阐述了为什么需要线程、Java中实现线程的几种方式,线程的多种状态切换;然后,介绍了为什么需要线程池,JDK自带的线程池实现方式ThreadPoolExecutor的使用及其...
1. 降低创建线程和销毁线程的性能开销 2. 提高响应速度,当有新任务需要执行是不需要等待线程创建就可以立马执行 3. 合理的设置线程池大小可以避免因为线程数超
里面是自己收藏的几篇关于线程的文档资料,大家可以看看哈
在前面的文章中,我们...我们来详细讲解一下Java的线程池,首先我们从核心的ThreadPoolExecutor类中的方法讲起,然后再讲述它的实现原理,接着给出了它的使用示例,后讨论了一下如何合理配置线程池的大小。 以下是
龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...
JDK5提供的原子类的操作以及实现原理.mp4 Lock接口认识与使用.mp4 手动实现一个可重入锁.mp4 AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示.mp4 读写锁认识与原理.mp4 细读...