JAVA 线程池实战

JAVA 线程实战

一、为什么要使用线程池?

构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。实际上,对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。每个请求对应一个线程(thread-per-request)方法的不足之一是:为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。


除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。

线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。


二、JAVA API 线程池

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

比较重要的几个类:

ExecutorService

真正的线程池接口。

ScheduledExecutorService

能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

ThreadPoolExecutor

ExecutorService的默认实现。

ScheduledThreadPoolExecutor

继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

1. newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2.newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3. newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4.newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。


三、项目实战

1、项目需求

我们目前的项目需要做一个影像切分的服务,获取到影像就开始把影像切割成不同的切片。为了提高切分的效率,需要开启多个线程来同时处理,这里就用到线程池的概念。下面把用到线程池的部分的Demo整理处理出来,大家参考一下,有好的建议希望及时提出!

2、线程池管理工具类(ThreadPoolManager)

package com.yzj.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolManager {

	// 私有的默认构造器
	private ThreadPoolManager () {}
	
	private static ThreadPoolManager poolManager = null;
	private static ExecutorService pool = null;
	
	// 对外提供单一实例
	public synchronized static ThreadPoolManager getInstance() {
		
		if(poolManager == null){
			
			poolManager = new ThreadPoolManager();
			pool = Executors.newFixedThreadPool(3);
		}
		
		return poolManager;
	}
	
	public ExecutorService getPool () {
		
		if(pool == null){
			
			pool = Executors.newFixedThreadPool(10);
		}
		
		return pool;
	}
}

3、切分影像线程(CutImageTread)

package com.yzj.pool;

import java.io.File;

import com.yzj.entity.CutInfo;
import com.yzj.util.ImageUtils;

public class CutImageThread implements Runnable {
	
	// 切片信息
	private CutInfo cutInfo;
	private File imageFile;
	
	public CutImageThread (CutInfo cutInfo, File imageFile) {
		
		this.cutInfo = cutInfo;
		this.imageFile = imageFile;
	}

	public void run() {

		double x = Double.parseDouble(cutInfo.getdX());
		double y = Double.parseDouble(cutInfo.getdY());
		double w = Double.parseDouble(cutInfo.getdW());
		double h = Double.parseDouble(cutInfo.getdH());
		
		// 切片路径
		String cutPath = "D:/CACHE_PATH/CUT" + File.separator + cutInfo.getCutName();
		
		boolean bool = ImageUtils.cutImage(x, y, w, h, imageFile, cutPath);
		
		System.out.println(Thread.currentThread().getName() + "    "+ imageFile.getName() + "    "+bool);
	}
}

4、切分影像工具类(ImageUtils)

package com.yzj.util;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.io.File;

import javax.imageio.ImageIO;

 /**
  * 影像操作工具类
  * @author Administrator
  *
  */
public class ImageUtils {
	
	public static boolean cutImage(double d_x, double d_y, double d_w, double d_h,
			File srcFile, String destFilePath) {
		
		boolean bool = false;
		
		try {
			
			// 读取源图像
			// 读取源图像
            BufferedImage bi = ImageIO.read(srcFile);
            int srcWidth =  bi.getWidth(); // 源图宽度
            int srcHeight =  bi.getHeight();// 源图高度
            
            if (srcWidth > 0 && srcHeight > 0) {
            	
            	int x = (int)(d_x * srcWidth); 
            	int y = (int)(d_y * srcHeight); 
            	int w = (int)(d_w * srcWidth) + 1; 
            	int h = (int)(d_h * srcHeight) + 1; 
            	
                Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
                // 四个参数分别为图像起点坐标和宽高
                CropImageFilter cropFilter = new CropImageFilter(x, y, w, h);
                Image img = Toolkit.getDefaultToolkit().createImage(
                        new FilteredImageSource(image.getSource(),
                                cropFilter));
                BufferedImage tag = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
                Graphics g = tag.getGraphics();
                g.drawImage(img, 0, 0, w, h, null); // 绘制切割后的图
                g.dispose();
                
                File destFile = new File(destFilePath);
                
                if(!destFile.getAbsoluteFile().exists()){
                	
                	destFile.getAbsoluteFile().mkdirs();
                }
                
                // 输出为文件
				ImageIO.write(tag, "JPEG", destFile);
            }
            
            bool = true;
		} catch (Exception e) {
			
			e.printStackTrace();
		}
		
		return bool;
	}
}

5、测试类(TestDemo)

package com.yzj.demo;

import java.io.File;

import com.yzj.entity.CutInfo;
import com.yzj.pool.CutImageThread;
import com.yzj.pool.ThreadPoolManager;

public class TestDemo {

	public static void main(String[] args) {
		
		File file = new File("D:/CACHE_PATH/IMG");
		
		File[] files = file.listFiles();
		
		if(files!=null && files.length>0){
			
			for(int i=0; i<files.length; i++){
				
				File imageFile = files[i];
				
				CutInfo cutInfo = new CutInfo();
				cutInfo.setdX("0.5785074626865672");
				cutInfo.setdY("0.5283340434597358");
				cutInfo.setdW("0.3838805970149254");
				cutInfo.setdH("0.02982530890498509");
				
				String imageName = imageFile.getName();
				String cutName = imageName.substring(0, imageName.indexOf(".")) + "_cut.jpg";
				cutInfo.setCutName(cutName);
				
				ThreadPoolManager.getInstance().getPool().execute(new CutImageThread(cutInfo, imageFile));
			}
		}
	}
}


6、影像路径如下


7、运行TestDemo执行结果如下

pool-1-thread-3    IMG100000001 (11).jpg    true
pool-1-thread-1    IMG100000001 (1).jpg    true
pool-1-thread-2    IMG100000001 (10).jpg    true
pool-1-thread-3    IMG100000001 (12).jpg    true
pool-1-thread-2    IMG100000001 (14).jpg    true
pool-1-thread-1    IMG100000001 (13).jpg    true
pool-1-thread-2    IMG100000001 (16).jpg    true
pool-1-thread-3    IMG100000001 (15).jpg    true
pool-1-thread-1    IMG100000001 (17).jpg    true
pool-1-thread-2    IMG100000001 (18).jpg    true
pool-1-thread-3    IMG100000001 (19).jpg    true
pool-1-thread-1    IMG100000001 (2).jpg    true
pool-1-thread-2    IMG100000001 (20).jpg    true
pool-1-thread-3    IMG100000001 (3).jpg    true
pool-1-thread-1    IMG100000001 (4).jpg    true
pool-1-thread-2    IMG100000001 (5).jpg    true
pool-1-thread-3    IMG100000001 (6).jpg    true
pool-1-thread-1    IMG100000001 (7).jpg    true
pool-1-thread-2    IMG100000001 (8).jpg    true
pool-1-thread-3    IMG100000001 (9).jpg    true




  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值