`

Semaphore控制高并发下载导致内存溢出问题

阅读更多

        在项目实际应用中,由于下载文件内容都比较大,如果同时有很多用户同时在下载,JVM的内存就会升的很高,甚至崩溃。为了避免很多用户同时下载,特引入Semaphore控制一次最多有配置个线程能进入实际下载的代码,即而控制JVM内存不会升的很高而导致崩溃。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component("DownloadView.csv")
public class DownloadView extends AbstractCsvView implements InitializingBean {
	//允许的最大线程数
	private String threadNum=PropertyUtil.getProperty("threadNum");
	// 线程池
    private ExecutorService exec = null;
    // 只能threadNum个线程同时访问
    private Semaphore semp = new Semaphore(Integer.parseInt(threadNum), true);
    
    @Override
    protected void buildExcelDocument(Map<String, Object> model, List<String> csvList, HttpServletRequest request,
            HttpServletResponse response) throws Exception  {
    	String fileName ="";
        String url = request.getParameter("outputInfo");
        if(StringUtil.isNotEmpty(url)){
        	fileName= url.substring(url.lastIndexOf("/") + 1, url.length());
        }
        super.setUrl(url);
        try {
			response.setHeader("Content-Disposition",
			        "attachment;filename=" + URLEncoder.encode(fileName, response.getCharacterEncoding()));
		} catch (UnsupportedEncodingException e) {
			throw new Exception("不支持此编码格式");
		}
    }
    
    @SuppressWarnings("unchecked")
    @Override
    protected void renderMergedOutputModel(final Map<String, Object> model, HttpServletRequest request,
            HttpServletResponse response) throws Exception, IOException, InterruptedException, ExecutionException  {
    	
    	exec = Executors.newCachedThreadPool();
    	response.setContentType(getContentType());
    	final String url = request.getParameter("outputInfo");
    	final ServletOutputStream out=response.getOutputStream();
    	final HttpServletRequest _request = request;
    	final HttpServletResponse _response = response;
    	InputStream in=null;
    	try{
    		in= new FileInputStream(url);
    	}catch(Exception e){
    		throw new Exception("找不到对应的文件:"+url);
    	}
    	final InputStream fis=in;
    	final String encode=super.getEncoding();
    	Callable<Boolean> call = new Callable<Boolean>() {
        	@Override
            public Boolean call() {
                try {
                    // 获取许可
                    semp.acquire();
                    List<String> csvList = null;
                    //IOUtils.readLines()是一次性读取整个文件
                    //readline() 和 .readlines()之间的差异是后者一次读取整个文件,像read()一样。
                    //readlines()自动将文件内容分析成一个行的列表,
                    //readline()每次只读取一行,通常比 readlines()慢得多。
                    //仅当没有足够内存可以一次读取整个文件时,才应该使用readline().
            		csvList = IOUtils.readLines(fis);
                    buildExcelDocument(model, csvList, _request, _response);
                    if (encode == null) {
            			IOUtils.writeLines(csvList,encode, out);
                    } else {
            			IOUtils.writeLines(csvList, encode, out, encode);
                    }
                    //Thread.sleep((long) (2000));
                    return true;
                }  catch (Exception e) {
                	System.out.println(e);
					return false;
				} finally {
					semp.release();
				}
            }
        };
        Future<Boolean> future=null;
        if(!exec.isShutdown()){
        	future= exec.submit(call);
        }
       exec.shutdown();
	   if((Boolean) future.get()){
		   System.out.println("success");
	   }else{
		   System.out.print("fail");
	   }
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
    }
}

AbstractCsvView.java

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.LocalizedResourceHelper;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.AbstractView;

public abstract class AbstractCsvView  extends AbstractView{

	/** The content type for an csv response */
    private static final String CONTENT_TYPE = "text/csv";
    
    /** The extension to look for existing templates */
    private static final String EXTENSION = ".csv";
    
    private String lineEnding;
    
    private String encoding;
    
    /** The url at which the template to use is located */
    private String url;
    
    /**
     * Default Constructor.
     * Sets the content type of the view to "text/csv".
     */
    public AbstractCsvView() {
            setContentType(CONTENT_TYPE);
    }
    
    /**
     * Set the URL of the Excel workbook source, without localization part nor extension.
     */
    public void setUrl(String url) {
            this.url = url;
    }
    
    
    public void setLineEnding(String lineEnding) {
            this.lineEnding = lineEnding;
    }

    public void setEncoding(String encoding) {
            this.encoding = encoding;
    }
    
    

    public String getLineEnding() {
        return lineEnding;
    }

    public String getEncoding() {
        return encoding;
    }

    public String getUrl() {
        return url;
    }

    @Override
    protected boolean generatesDownloadContent() {
            return true;
    }
    
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model,
                    HttpServletRequest request, HttpServletResponse response)
                    throws Exception {
            // Set the content type and get the output stream.
            response.setContentType(getContentType());
            List<String> csvList = null;
            if (this.url != null) {
                    InputStream in = getTemplateSource(this.url, request);
                    csvList = IOUtils.readLines(in);
            }else{
                    csvList = new ArrayList<String>();
            }
            buildExcelDocument(model, csvList, request, response);
            if(this.encoding == null){
                    IOUtils.writeLines(csvList, this.lineEnding, response.getOutputStream());
            }else{
                    IOUtils.writeLines(csvList, this.lineEnding, response.getOutputStream(), this.encoding);
            }
    }

    protected InputStream getTemplateSource(String url, HttpServletRequest request) throws IOException {
            LocalizedResourceHelper helper = new LocalizedResourceHelper(getApplicationContext());
            Locale userLocale = RequestContextUtils.getLocale(request);
            Resource inputFile = helper.findLocalizedResource(url, EXTENSION, userLocale);
            
            // Create the Excel document from the source.
            if (logger.isDebugEnabled()) {
                    logger.debug("Loading Excel workbook from " + inputFile);
            }
            return inputFile.getInputStream();
    }
    
    /**
     * Subclasses must implement this method to create an csv List
     * document, given the model.
     * @param model the model Map
     * @param csvList
     * @param request in case we need locale etc. Shouldn't look at attributes.
     * @param response in case we need to set cookies. Shouldn't write to it.
     * @throws Exception in case of failure
     */
    protected abstract void buildExcelDocument(Map<String, Object> model, List<String> csvList,
                    HttpServletRequest request, HttpServletResponse response) throws Exception;

}

 

分享到:
评论

相关推荐

    易语言内存配置项[线程安全、高效读写、防溢出]

    在易语言中,可以通过以下几种方式来防止内存溢出: 1. 使用动态内存分配函数:易语言提供了如`创建内存块`、`扩展内存块`等函数,允许程序在运行时按需分配和释放内存,避免静态分配导致的浪费和溢出风险。 2. ...

    java高并发系统设计

    - **栈内存与栈溢出**:每个线程都有独立的栈,过多的递归或线程可能导致栈溢出。 - **线程优先级**:虽然Java线程优先级可以设置,但在实际应用中并不推荐依赖它来调整并发行为。 7. **分布式并发** - **分布式...

    1工作临时-servlet 多线程问题

    Servlet多线程问题是一个常见的挑战,尤其是在高并发的服务器环境中。这里我们将深入探讨Servlet与多线程的相关知识点,以及如何解决可能遇到的问题。 首先,理解Servlet生命周期中的多线程模型至关重要。当一个...

    加载大图片、内存缓存、diskCache

    大图片如果直接加载到内存中,可能会导致内存溢出(OOM)问题。因此,我们需要通过缩放图片来减少内存消耗。使用`BitmapFactory.Options`类的`inSampleSize`参数可以指定图片的缩小比例,以降低内存占用。例如,设置...

    Java八股文之JVM与多线程

    线程安全问题(如死锁、活锁、饥饿等)需要谨慎处理,合理使用并发工具类如Semaphore、CountDownLatch、CyclicBarrier等。 8. **线程池**: Java的ExecutorService和ThreadPoolExecutor提供了线程池管理,可以有效...

    Java.Bug模式详解

    不当的内存管理可能导致内存溢出。理解Java内存模型,合理设置堆大小,避免创建过大的对象,及时清理不再使用的对象,使用WeakReference和SoftReference管理对象,可以帮助防止内存溢出。 以上仅是部分常见的Java ...

    A-JUC-JVM-Java并发知识..pdf

    了解内存溢出的原因和如何调试和处理这些问题对于Java开发人员非常重要。 #### 性能评估与诊断 在开发和维护Java应用程序时,性能评估和问题诊断是不可或缺的。了解如何使用top、vmstat、free、df、iostat、ifstat...

    不是我吹!高并发和分布式,看这篇就够了!

    控制访问速率的另一种算法是漏桶算法,其原理类似固定容量的桶,水(请求)以一定速率流入,当流入速度过快导致水溢出时,就限制了数据的传输速率。漏桶算法能够对数据传输速率实现硬性限制,适用于对流量控制要求高...

    swoole-shared-memory-master.zip

    5. **Semaphore**: 信号量用于限制同时访问共享内存的进程数量,避免资源竞争导致的问题。 在"Swoole Shared Memory-master"这个压缩包中,我们可以期待找到以下内容: - `README.md`: 项目介绍、安装指南、使用...

    java并发个人心得

    - **避免过度使用线程**:创建过多的线程不仅会导致上下文切换频繁,增加系统开销,还可能引起内存溢出等问题。 - **利用并发框架**:利用成熟的并发框架如`Java.util.concurrent`包中的工具类可以大大简化并发编程...

    C语言安全编程课程讲稿

    竞态条件(Race Conditions)是在多线程编程中常见的问题,当两个或多个线程并发访问同一资源并依赖于特定的执行顺序时,可能会导致未定义的行为。同步原语如mutex和semaphore可以帮助解决这类问题,确保对共享资源...

    关于Java开发的五百个常见问题

    15. **并发编程**:Java并发工具类如Semaphore、CyclicBarrier等,以及并发集合如ConcurrentHashMap,都是解决多线程环境下问题的关键。 以上只是部分可能包含在"Java五百篇"中的主题,每个主题下都可能有几十甚至...

    操作系统常见笔试面试题

    缓冲区溢出是指程序在向缓冲区写入数据时超过了其实际容量,导致数据溢出到缓冲区之外的内存区域。这可能导致数据破坏、程序崩溃,甚至恶意攻击者利用该漏洞执行任意代码,危害系统的安全性。 以上就是针对操作系统...

    2023年面试记录汇总

    - **程序计数器**:记录当前线程执行的字节码指令的行号,用于线程切换后的恢复,是线程私有的,不会出现内存溢出(OutOfMemoryError)。 - **Java虚拟机栈**:每个线程都有一个栈,用于存储方法调用的栈帧,包含...

    java长时间运行任务式

    例如,可以使用ThreadPoolExecutor来创建一个具有固定大小线程池的ExecutorService,这样可以确保系统不会因创建过多线程而导致内存溢出。 另外,Java还提供了其他高级特性来处理并发和多线程问题,如: - **同步...

    最新大厂Java面试题(上).pdf

    内存溢出异常指的是程序在申请内存时,没有足够的空间分配给请求的内存区域,导致JVM无法完成存储操作。常见的内存溢出包括:堆溢出、栈溢出、方法区溢出。 #### 垃圾收集器 垃圾收集器主要作用是回收JVM堆中的...

    java性能调优

    通常情况下,过多的线程会导致内存溢出或其他性能问题。 - **线程池**: 使用线程池是一种更高效地管理线程的方式。`Executors.newCachedThreadPool()`是一个常用的创建线程池的方法,它可以根据实际需求动态调整线程...

    Java优化策略在铁路互联网售票系统中的应用研究.zip

    通过调整JVM的堆大小、新生代与老年代的比例、垃圾收集器类型等参数,可以优化系统资源的分配,避免内存溢出或频繁的垃圾回收导致的系统停顿。 其次,代码级别的优化也是不可或缺的。这包括减少不必要的对象创建,...

    一个从网络上收流保存到本地的程序

    - **缓冲溢出**:需要注意内存管理,防止缓冲区过大导致内存溢出,或过小无法接收完整数据。 3. **保存到本地文件**: - **文件I/O操作**:在Linux中,可以使用`open`打开文件,`write`写入数据,`close`关闭文件...

    关于《Java那么健壮,怎么会挂?》的1000条回复Jav

    9. **性能监控**:使用JConsole、VisualVM等工具监控JVM的运行状态,可以及时发现潜在问题,如CPU过高、内存溢出等。 10. **日志与调试**:良好的日志记录习惯可以帮助定位问题。学会使用断点、步进、查看堆栈信息...

Global site tag (gtag.js) - Google Analytics