精华内容
下载资源
问答
  • 什么是多线程?如何实现多线程

    万次阅读 多人点赞 2019-04-09 09:53:36
    【转】什么是线程安全?怎么实现线程安全?什么是进程?什么是线程什么是线程安全?添加一个状态呢?如何确保线程安全?synchronizedlock 转自:https://blog.csdn.net/csdnnews/article/details/82321777 什么是...


    转自:https://blog.csdn.net/csdnnews/article/details/82321777

    什么是进程?

    电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。比如下图中的QQ、酷狗播放器、电脑管家等等。在这里插入图片描述

    什么是线程?

    进程想要执行任务就需要依赖线程。换句话说,就是进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。

    那什么是多线程?提到多线程这里要说两个概念,就是串行和并行,搞清楚这个,我们才能更好地理解多线程。

    所谓串行,其实是相对于单条线程来执行多个任务来说的,我们就拿下载文件来举个例子:当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,也就是说,必须等下载完A之后才能开始下载B,它们在时间上是不可能发生重叠的。
    在这里插入图片描述
    并行:下载多个文件,开启多条线程,多个文件同时进行下载,这里是严格意义上的,在同一时刻发生的,并行在时间上是重叠的。
    在这里插入图片描述
    了解了这两个概念之后,我们再来说说什么是多线程。举个例子,我们打开腾讯管家,腾讯管家本身就是一个程序,也就是说它就是一个进程,它里面有很多的功能,我们可以看下图,能查杀病毒、清理垃圾、电脑加速等众多功能。

    按照单线程来说,无论你想要清理垃圾、还是要病毒查杀,那么你必须先做完其中的一件事,才能做下一件事,这里面是有一个执行顺序的。

    如果是多线程的话,我们其实在清理垃圾的时候,还可以进行查杀病毒、电脑加速等等其他的操作,这个是严格意义上的同一时刻发生的,没有执行上的先后顺序。
    在这里插入图片描述
    以上就是,一个进程运行时产生了多个线程。

    在了解完这个问题后,我们又需要去了解一个使用多线程不得不考虑的问题——线程安全。

    今天我们不说如何保证一个线程的安全,我们聊聊什么是线程安全?因为我之前面试被问到了,说真的,我之前真的不是特别了解这个问题,我们好像只学了如何确保一个线程安全,却不知道所谓的安全到底是什么!

    什么是线程安全?

    当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
    既然是线程安全问题,那么毫无疑问,所有的隐患都是在多个线程访问的情况下产生的,也就是我们要确保在多条线程访问的时候,我们的程序还能按照我们预期的行为去执行,我们看一下下面的代码。

    Integer count = 0;
    public void getCount() {
           count ++;
           System.out.println(count);
     }
    

    很简单的一段代码,下面我们就来统计一下这个方法的访问次数,多个线程同时访问会不会出现什么问题,我开启的3条线程,每个线程循环10次,得到以下结果:
    在这里插入图片描述

    我们可以看到,这里出现了两个26,出现这种情况显然表明这个方法根本就不是线程安全的,出现这种问题的原因有很多。

    最常见的一种,就是我们A线程在进入方法后,拿到了count的值,刚把这个值读取出来,还没有改变count的值的时候,结果线程B也进来的,那么导致线程A和线程B拿到的count值是一样的。

    那么由此我们可以了解到,这确实不是一个线程安全的类,因为他们都需要操作这个共享的变量。其实要对线程安全问题给出一个明确的定义,还是蛮复杂的,我们根据我们这个程序来总结下什么是线程安全。

    当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。

    搞清楚了什么是线程安全,接下来我们看看Java中确保线程安全最常用的两种方式。先来看段代码。

    public void threadMethod(int j) {
    
        int i = 1;
    
        j = j + i;
    }
    
    

    大家觉得这段代码是线程安全的吗?

    毫无疑问,它绝对是线程安全的,我们来分析一下,为什么它是线程安全的?

    我们可以看到这段代码是没有任何状态的,就是说我们这段代码,不包含任何的作用域,也没有去引用其他类中的域进行引用,它所执行的作用范围与执行结果只存在它这条线程的局部变量中,并且只能由正在执行的线程进行访问。当前线程的访问,不会对另一个访问同一个方法的线程造成任何的影响。

    两个线程同时访问这个方法,因为没有共享的数据,所以他们之间的行为,并不会影响其他线程的操作和结果,所以说无状态的对象,也是线程安全的。

    添加一个状态呢?

    如果我们给这段代码添加一个状态,添加一个count,来记录这个方法并命中的次数,每请求一次count+1,那么这个时候这个线程还是安全的吗?

    public class ThreadDemo {
    
       int count = 0; // 记录方法的命中次数
    
       public void threadMethod(int j) {
    
           count++ ;
    
           int i = 1;
    
           j = j + i;
       }
    }
    
    

    明显已经不是了,单线程运行起来确实是没有任何问题的,但是当出现多条线程并发访问这个方法的时候,问题就出现了,我们先来分析下count+1这个操作。

    进入这个方法之后首先要读取count的值,然后修改count的值,最后才把这把值赋值给count,总共包含了三步过程:“读取”一>“修改”一>“赋值”,既然这个过程是分步的,那么我们先来看下面这张图,看看你能不能看出问题:
    在这里插入图片描述
    可以发现,count的值并不是正确的结果,当线程A读取到count的值,但是还没有进行修改的时候,线程B已经进来了,然后线程B读取到的还是count为1的值,正因为如此所以我们的count值已经出现了偏差,那么这样的程序放在我们的代码中,是存在很多的隐患的。

    如何确保线程安全?

    既然存在线程安全的问题,那么肯定得想办法解决这个问题,怎么解决?我们说说常见的几种方式

    synchronized

    synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保我们数据的完整性,使用方法一般是加在方法上。

    public class ThreadDemo {
    
       int count = 0; // 记录方法的命中次数
    
       public synchronized void threadMethod(int j) {
    
           count++ ;
    
           int i = 1;
    
           j = j + i;
       }
    }
    
    

    这样就可以确保我们的线程同步了,同时这里需要注意一个大家平时忽略的问题,首先synchronized锁的是括号里的对象,而不是代码,其次,对于非静态的synchronized方法,锁的是对象本身也就是this。

    当synchronized锁住一个对象之后,别的线程如果想要获取锁对象,那么就必须等这个线程执行完释放锁对象之后才可以,否则一直处于等待状态。

    注意点:虽然加synchronized关键字,可以让我们的线程变得安全,但是我们在用的时候,也要注意缩小synchronized的使用范围,如果随意使用时很影响程序的性能,别的对象想拿到锁,结果你没用锁还一直把锁占用,这样就有点浪费资源。

    lock

    先来说说它跟synchronized有什么区别吧,Lock是在Java1.6被引入进来的,Lock的引入让锁有了可操作性,什么意思?就是我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性,但是从使用上说Lock明显没有synchronized使用起来方便快捷。我们先来看下一般是如何使用的:

    private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
    
       private void method(Thread thread){
           lock.lock(); // 获取锁对象
           try {
               System.out.println("线程名:"+thread.getName() + "获得了锁");
               // Thread.sleep(2000);
           }catch(Exception e){
               e.printStackTrace();
           } finally {
               System.out.println("线程名:"+thread.getName() + "释放了锁");
               lock.unlock(); // 释放锁对象
           }
       }
    
    

    进入方法我们首先要获取到锁,然后去执行我们业务代码,这里跟synchronized不同的是,Lock获取的所对象需要我们亲自去进行释放,为了防止我们代码出现异常,所以我们的释放锁操作放在finally中,因为finally中的代码无论如何都是会执行的。

    写个主方法,开启两个线程测试一下我们的程序是否正常:

    public static void main(String[] args) {
           LockTest lockTest = new LockTest();
    
           // 线程1
           Thread t1 = new Thread(new Runnable() {
    
               @Override
               public void run() {
                   // Thread.currentThread()  返回当前线程的引用
                   lockTest.method(Thread.currentThread());
               }
           }, "t1");
    
           // 线程2
           Thread t2 = new Thread(new Runnable() {
    
               @Override
               public void run() {
                   lockTest.method(Thread.currentThread());
               }
           }, "t2");
    
           t1.start();
           t2.start();
       }
    
    

    结果
    在这里插入图片描述
    可以看出我们的执行,是没有任何问题的。

    其实在Lock还有几种获取锁的方式,我们这里再说一种,就是tryLock()这个方法跟Lock()是有区别的,Lock在获取锁的时候,如果拿不到锁,就一直处于等待状态,直到拿到锁,但是tryLock()却不是这样的,tryLock是有一个Boolean的返回值的,如果没有拿到锁,直接返回false,停止等待,它不会像Lock()那样去一直等待获取锁。

    我们来看下代码:

    private void method(Thread thread){
           // lock.lock(); // 获取锁对象
           if (lock.tryLock()) {
               try {
                   System.out.println("线程名:"+thread.getName() + "获得了锁");
                   // Thread.sleep(2000);
               }catch(Exception e){
                   e.printStackTrace();
               } finally {
                   System.out.println("线程名:"+thread.getName() + "释放了锁");
                   lock.unlock(); // 释放锁对象
               }
           }
       }
    
    

    结果:我们继续使用刚才的两个线程进行测试可以发现,在线程t1获取到锁之后,线程t2立马进来,然后发现锁已经被占用,那么这个时候它也不在继续等待。

    在这里插入图片描述
    似乎这种方法,感觉不是很完美,如果我第一个线程,拿到锁的时间,比第二个线程进来的时间还要长,是不是也拿不到锁对象?

    那我能不能,用一中方式来控制一下,让后面等待的线程,可以等待5秒,如果5秒之后,还获取不到锁,那么就停止等,其实tryLock()是可以进行设置等待的相应时间的。

    private void method(Thread thread) throws InterruptedException {
           // lock.lock(); // 获取锁对象
    
           // 如果2秒内获取不到锁对象,那就不再等待
           if (lock.tryLock(2,TimeUnit.SECONDS)) {
               try {
                   System.out.println("线程名:"+thread.getName() + "获得了锁");
    
                   // 这里睡眠3秒
                   Thread.sleep(3000);
               }catch(Exception e){
                   e.printStackTrace();
               } finally {
                   System.out.println("线程名:"+thread.getName() + "释放了锁");
                   lock.unlock(); // 释放锁对象
               }
           }
       }
    
    

    结果:看上面的代码,我们可以发现,虽然我们获取锁对象的时候,可以等待2秒,但是我们线程t1在获取锁对象之后,执行任务缺花费了3秒,那么这个时候线程t2是不在等待的。
    在这里插入图片描述
    我们再来改一下这个等待时间,改为5秒,再来看下结果:

    private void method(Thread thread) throws InterruptedException {
           // lock.lock(); // 获取锁对象
    
           // 如果5秒内获取不到锁对象,那就不再等待
           if (lock.tryLock(5,TimeUnit.SECONDS)) {
               try {
                   System.out.println("线程名:"+thread.getName() + "获得了锁");
               }catch(Exception e){
                   e.printStackTrace();
               } finally {
                   System.out.println("线程名:"+thread.getName() + "释放了锁");
                   lock.unlock(); // 释放锁对象
               }
           }
       }
    
    

    结果:这个时候我们可以看到,线程t2等到5秒获取到了锁对象,执行了任务代码。
    在这里插入图片描述
    以上就是使用Lock,来保证我们线程安全的方式。

    展开全文
  • opencv多线程显示的问题解决方法

    千次阅读 2020-04-18 11:36:23
    opencv多线程显示的问题解决方法 1.问题描述 由于业务需求,需要在多线程中,使用OpenCV读取图片并显示,实现很简单,只需要调用python的threading创建线程,并在线程中显示图片即可,但会出现一个问题: 只能显示第...

    opencv多线程显示的问题和解决方法

     


    1.问题描述

    由于业务需求,需要在多线程中,使用OpenCV读取图片并显示,实现很简单,只需要调用python的threading创建线程,并在线程中显示图片即可,但会出现一个问题: 只能显示第一张图图片,第二张图图片就会阻塞掉!!!

    这是最初的精简的BUG代码

    
    import os
    import cv2
    import glob
    import threading
    
    
    class DisplayTask(object):
        """
        """
    
        def __init__(self, ):
            """
            :param width:
            :param height:
            """
            self.win_name = "frame"
    
        def show(self, image_path):
            print("block here 1")
            frame = cv2.imread(image_path)
            print("block here 2")
            cv2.imshow(self.win_name, frame)
            print("block here 3")
            cv2.waitKey(1000)
            cv2.destroyAllWindows()
    
    
    def display_task(th_id, image_path):
        print("start thread-{}".format(th_id))
        dt = DisplayTask()
        dt.show(image_path)
    
    
    def demo(image_dir):
        image_list = glob.glob(os.path.join(image_dir, "*.jpg"))
        print("have image:{}".format(len(image_list)))
        for th_id, image_path in enumerate(image_list):
            thread = threading.Thread(target=display_task, args=(th_id, image_path,))
            thread.start()
            # 等待线程执行完毕
            thread.join()
            print("finish thread-{}".format(th_id))
    
    
    if __name__ == "__main__":
        image_dir = "/media/dm/dm1/git/python-learning-notes/data"
        demo(image_dir)
    

    运行结果:

    have image:4
    start thread-0
    block here 1
    block here 2
    block here 3
    finish thread-0
    start thread-1
    block here 1
    block here 2

     存在问题: 第一次启动线程,可以正常显示图片,但第二个线程就会在cv2.imshow(self.win_name, frame)这里阻塞,不会执行下去

     


    2.解决方法

    opencv中imshow等显示操作只能在主线程中显示,不能在子线程中显示,那为什么第1个子线程可以正常显示咧? 原因嘛?

    解决方法也很简单,既然不能在子线程中显示,但没说不能在多进程中显示呀

    所有把threading改为multiprocessing,问题解决:

    
    # -*-coding: utf-8 -*-
    """
        @Project: python-learning-notes
        @Author : panjq
        @E-mail : pan_jinquan@163.com
        @Date   : 2020-04-10 18:24:06
    """
    
    import os
    import cv2
    import glob
    # import threading
    import multiprocessing
    
    
    class DisplayTask(object):
        """
        """
    
        def __init__(self, ):
            """
            :param width:
            :param height:
            """
            self.win_name = "frame"
    
        def show(self, image_path):
            print("block here 1")
            frame = cv2.imread(image_path)
            print("block here 2")
            cv2.imshow(self.win_name, frame)
            print("block here 3")
            cv2.waitKey(1000)
            cv2.destroyAllWindows()
    
    
    def display_task(th_id, image_path):
        print("start thread-{}".format(th_id))
        dt = DisplayTask()
        dt.show(image_path)
    
    
    def demo(image_dir):
        image_list = glob.glob(os.path.join(image_dir, "*.jpg"))
        print("have image:{}".format(len(image_list)))
        for th_id, image_path in enumerate(image_list):
            # thread = threading.Thread(target=display_task, args=(th_id, image_path,))
            thread = multiprocessing.Process(target=display_task, args=(th_id, image_path,))
            thread.start()
            # 等待线程执行完毕
            thread.join()
            print("finish thread-{}".format(th_id))
    
    
    if __name__ == "__main__":
        image_dir = "/media/dm/dm1/git/python-learning-notes/data"
        demo(image_dir)
    

     

     

    展开全文
  • ThreadLocal解决多线程并发问题

    千次阅读 2018-03-12 22:45:56
    在java程序中,常用的有两种机制来解决多线程并发问题,一种是sychronized方式,通过锁机制,一个线程执行时,让另一个线程等待,是以时间换空间的方式来让多线程串行执行。而另外一种方式就是ThreadLocal方式,通过...

        在java程序中,常用的有两种机制来解决多线程并发问题,一种是sychronized方式,通过锁机制,一个线程执行时,让另一个线程等待,是以时间换空间的方式来让多线程串行执行。而另外一种方式就是ThreadLocal方式,通过创建线程局部变量,以空间换时间的方式来让多线程并行执行。两种方式各有优劣,适用于不同的场景,要根据不同的业务场景来进行选择。

        在spring的源码中,就使用了ThreadLocal来管理连接,在很多开源项目中,都经常使用ThreadLocal来控制多线程并发问题,因为它足够的简单,我们不需要关心是否有线程安全问题,因为变量是每个线程所特有的。我自己也写了一个小例子,仅供大家参考,来、证明一下ThreadLocal对多线程并发问题的控制。

       

    package com.threadlocal;
    /**
     * ThreadLocal 线程局部变量
     * @author 24572
     *
     */
    public class TestThreadLocal {
    	private static ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>(){
    		@Override
    		protected Integer initialValue() {
    			return 0;
    		}
    	};
    	
    	public Integer getNext(){
    		threadLocal.set(threadLocal.get()+1);
    		return threadLocal.get();
    	}
    	
    	public static void main(String[] args) {
    		TestThreadLocal testThreadLocal=new TestThreadLocal();
    		TestThread t1=new TestThread(testThreadLocal);
    		TestThread t2=new TestThread(testThreadLocal);
    		TestThread t3=new TestThread(testThreadLocal);
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    	
    	public static class TestThread extends Thread{
    		
    		private TestThreadLocal testThreadLocal;
    		
    		
    		public TestThread(com.threadlocal.TestThreadLocal testThreadLocal) {
    			this.testThreadLocal = testThreadLocal;
    		}
    
    
    		@Override
    		public void run() {
    			for(int i=0;i<3;i++){
    				System.out.println("thread:"+Thread.currentThread().getName()+"next:"+testThreadLocal.getNext());
    			}
    		}
    	}
    }
    

    运行结果是:

    thread:Thread-0next:1
    thread:Thread-0next:2
    thread:Thread-0next:3
    thread:Thread-2next:1
    thread:Thread-2next:2
    thread:Thread-2next:3
    thread:Thread-1next:1
    thread:Thread-1next:2
    thread:Thread-1next:3
    
    很明显的可以看到,虽然我用的是同一个对象,但是每次都是打印的123,并没有引发线程安全问题。
    展开全文
  • 多线程解决导出excel性能问题

    万次阅读 2017-01-17 20:17:39
    多线程解决导出excel性能问题 第一步:Controller 发起导出数据请求 @RequestMapping(value = "/subpolicy/hdevpayback/exportOtherExcelAll.json") public void exportOtherExcelAll(final HttpServletRequest ...

    多线程解决导出excel性能问题

    第一步:Controller  发起导出数据请求

     @RequestMapping(value = "/subpolicy/hdevpayback/exportOtherExcelAll.json")
      public void exportOtherExcelAll(final HttpServletRequest request, final HttpServletResponse response,
          String statDate, String uuId) {
        if (!LockUtils.getLock("exportHardDevExcelAll", 180)) {
          try {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("其他用户正在导出,请稍后再试...");
            response.getWriter().flush();
          } catch (Exception e) {
            ExceptionUtils.throwBusinessException(getClass(), "返回消息异常", e);
          }
          return;
        }
        try {
          RedisUtil.setKey(uuId, "1", 120);
          String file =
              hardDevPayBackService.exportOtherHardExcelAll(statDate, Constants.EXPORT_MODE_LOCAL, request.getSession()
                  .getServletContext().getRealPath("/"));
          FileUtils.exportFile(response, new File(file), "其他设备返款全量导出", "csv");
          RecordLogManager.getInstance().commitOriginalLog(MyJedisCon.getRedisUser(request).getUserName(),
              "HDEVPAYBACK_MENU", "exportAll", "设备返款全部导出");
          RedisUtil.deleteKey(uuId);
        } finally {
          LockUtils.unlock("exportHardDevExcelAll");
        }
      }


    第二步:计算总记录数,根据总记录数分配线程数和每个线程处理的记录数。

     @Override
      public String exportOtherHardExcelAll(String statDate, int mode, String filePath) {
        String csvFilePath = filePath + "exportTmp/hardDevAll/";
        if (StringUtils.isBlank(statDate)) {
          statDate = DateUtil.getYesterday();
        }
        String[] fields = {"payState", "payDate", "payNum", "payMoney", "payMark", "isPayBack", "devNumber", "devType",
            "equipNum", "devModel", "agentNumber", "agentName", "schoolAccount", "schoolName", "price", "payAgentName",
            "payAgentNumber", "paySchoolName", "paySchoolAccount", "paySchoolNumber", "formalTime", "bindTime", "category"};
        String[] head = {"返款状态", "返款时间", "返款数量", "返款金额", "说明", "可否返款", "设备编号", "设备类型", "数量", "设备型号", "代理商编号", "代理商名称",
            "幼儿园账号", "幼儿园名称", "价格", "返款代理商", "返款代理商编号", "返款幼儿园", "返款幼儿园账号", "返款幼儿园id", "幼儿园转正时间", "绑定时间", "幼儿园类别"};
        int count = countOtherExcelAll(statDate);//计算总记录数
        int[] indexs = ThreadUtils.getIndex(count, 10000, 5);//根据总记录数分配线程数和每个线程处理的记录数
        CountDownLatch latch = new CountDownLatch(indexs.length - 1);
        for (int j = 1; j < indexs.length; j++) {
          taskExecutor.execute(new HardDevExportThread(latch, Constants.THREAD_TYPE_OTHER_HARD_DEV, j, csvFilePath,
              statDate, indexs[j - 1], indexs[j] - indexs[j - 1], fields));
        }
        String exportFilePath = getOneCsv(latch, csvFilePath, head, indexs.length);
        return exportFilePath;
      }

    附:ThreadUtils.getIndex方法,最终结果 [0,10000,20000,30000,40000,50000]

    public class ThreadUtils {
        /**
         * 返回每个线程的数据下标始末,限制最大线程数
         * @param size 总数
         * @param minSize 单个线程最小执行数量
         * @param maxTask 最大线程数
         * @return
         */
        public static int[] getIndex(int size, int minSize, int maxTask) {
            int listIndexCount;
            double sizeDb = (double) size, minSizeDb = (double) minSize, maxTaskDb = (double) maxTask;
            if (sizeDb / minSizeDb < maxTaskDb) {
                listIndexCount = Double.valueOf(Math.ceil(sizeDb / minSizeDb)).intValue();
            } else {
                listIndexCount = maxTask;
            }
            int each = Double.valueOf(Math.floor(sizeDb / listIndexCount)).intValue();
            int[] indexs = new int[listIndexCount + 1];
            indexs[0] = 0;
            int totalCount = 0;
            for (int i = 1; i < listIndexCount; i++) {
                indexs[i] = indexs[i - 1] + each;
                totalCount += each;
            }
            // 最后一个线程可能多分担一点
            indexs[listIndexCount] = size - totalCount + indexs[listIndexCount - 1];
            return indexs;
        }
    }

    第三步:每个任务处理的事情  取数和导出到excel文件(每个线程导出一个文件)

     @Override
      public void run() {
        try {
          int each = 10000;
          int times = Double.valueOf(Math.floor(size / each)).intValue();
          int totalCount = 0;
          for (int i = 0; i < times; i++) {
            int beforeCount = totalCount;
            totalCount += each;
            List list;
            if (DEV_EXPORT_THREAD.equals(threadType)) {//硬件返款和其他硬件返款
              list = hardDevPayBackService.getHardExcelAllByIndex(statDate, beforeCount + startIndex, each);//取数
            }
            else {
              list = hardDevPayBackService.getOtherHardExcelAllByIndex(statDate, beforeCount + startIndex, each);
            }
            ExcelExport.exportCsvLocal(filePath, threadNum.toString(), list, null, fields, i == 0);
          }
          if (totalCount < size) {// 额外冗余each条的limit,以防count有漏掉
            List list;
            if (DEV_EXPORT_THREAD.equals(threadType)) {
              list = hardDevPayBackService.getHardExcelAllByIndex(statDate, totalCount + startIndex, size - totalCount + each);
            }
            else {
              list = hardDevPayBackService.getOtherHardExcelAllByIndex(statDate, totalCount + startIndex, size - totalCount + each);
            }
            ExcelExport.exportCsvLocal(filePath, threadNum.toString(), list, null, fields, totalCount == 0);//写入excel文件
          }
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
          countDownLatch.countDown();
        }
      }


    第四步:把所有excel文件合并到一个文件

      private String getOneCsv(CountDownLatch latch, String filePath, String[] head, int fileCount) {
        BufferedReader reader = null;
        BufferedWriter writer = null;
        try {
          latch.await();//等待所有线程都完成才执行。
          File file = new File(filePath + "all.csv");
          if (file.exists() && !file.isDirectory()) {
            file.delete();
          }
          file.createNewFile();
          writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath + "all.csv", true), "GB2312"));
          for (int i = 0; i < head.length; i++) {
            writer.write("\"" + head[i] + "\"");
            if (i < head.length - 1) {
              writer.write(",");
            }
          }
          writer.write("\r\n");
          for (int i = 1; i < fileCount; i++) {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath + i + ".csv"), "GB2312"));
            char[] buffer = new char[1024];
            int len = 0;
            while ((len = reader.read(buffer)) > 0) {
              writer.write(buffer, 0, len);
            }
            reader.close();
            writer.flush();
          }
        } catch (Exception e) {
          e.printStackTrace();
          return null;
        } finally {
          try {
            if (reader != null) {
              reader.close();
            }
            writer.close();
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
        return filePath + "all.csv";
      }







    展开全文
  • 一、为什么使用多线程优于多进程: 1.创建一个线程比创建一个进程的代价要小; 2.线程的切换比进程间的切换代价小 ; 3.多线程可以充分利用多处理器 ; 4.线程间的通信比进程间的通信代价要小的多; 4.1数据共享 :...
  • 多线程解决窗口售票问题

    千次阅读 2016-12-11 17:26:25
    这是因为迅雷开启了多线程,加快了下载速度。  什么是进程?  进程就是正在运行的程序。开启QQ就是开启了一个进程,当一个进程进入内存运行,就变成了一个进程。进程是处于运行过程中的程序。  什么是线程?  ...
  • C# winform多线程问题解决总结

    千次阅读 2018-07-21 18:16:20
    一、多线程创建后,窗体出现假死 解决:创建线程之后,在下面代码加上一句:Application.DoEvents();即可。 二、窗口关闭时,线程全部停止 解决:将其设置为后台进程:thread.IsBackground = true;但是有时需要...
  • Java多线程安全问题解决方案

    千次阅读 2016-03-16 22:17:48
    Java多线程安全问题解决方案 一、问题引入 通过最常见的多窗口售票问题引入线程安全的问题。代码如下: 注:这里使用Runnable接口来实现线程,这样做是为了共享代售票这个资源,如果我们使用继承Thread来操作,...
  • 面试题:线程是什么?多线程

    万次阅读 多人点赞 2018-10-27 10:52:48
    多线程的示例以及解决方案?线程池是什么? 一.线程是什么? 在Thread类中有这样的明确定义:线程是程序中执行的线程,Java虚拟机允许程序同时运行多个执行线程。 怎么创建一个线程呢? Thread中规定:有两种方法可以...
  • 什么老说python是伪多线程,怎么解决

    万次阅读 多人点赞 2019-05-20 14:43:56
    目录一、什么是多线程、多进程、守护线程1.1 进程1.2 程序1.3 线程1.4 多线程1.5 守护线程1.6 进程与线程的区别1.7 进程与线程的优缺点二、利用Python进行并行计算2.1、并行?伪并行?2.2 GIL2.2.1 GIL是什么2.3 ...
  • 解决多线程并发安全问题

    千次阅读 2019-02-16 20:16:49
    解决多线程的并发安全问题,java无非就是加锁,具体就是两个方法 (1) Synchronized(java自带的关键字) (2) lock 可重入锁 (可重入锁这个包java.util.concurrent.locks 底下有两个接口,分别对应两个类实现了这个...
  • 解决多线程并发问题

    千次阅读 2017-12-21 03:02:26
    如果对该表的更新或插入的操作,都会经过一个统一的文件,这种方式是可以解决进程并发的问题; 实现方式如下: public static function cbInventoryReserve() { $LOCK_FILE_PATH = $_SERVER['DOCUMENT_...
  • 就是 多线程环境中 , 且存在数据共享 , 一个线程访问的共享 数据被其他线程修改了, 那么就发生了线程安全问题 , 整个访问过程中 , 无一共享的数据被其他线程修改了 就是线程安全的 程序中如果使用成员变量, 且对成员...
  • //同时启动1000个线程,去进行i++计算,看看实际结果 for (int i = 0; i ; i++) { new Thread(new Runnable() { public void run() { Counter.inc(); } }).start(); } //这里...
  • C# 解决多线程随机数重复问题

    千次阅读 2019-12-07 10:35:09
    多线程同时执行结果很高概率相同, /// 是用的当前时间为seed,时间相同结果相同 /// /// 解决随机数重复问题 /// 同时模拟远程请求的随机延时 /// </summary> public class RandomH...
  • 多线程同时执行的时候可能出现不安全问题 当3个人同时拿到一个苹果,他们的编号就一样,当时主要看是谁先吃掉苹果 除非拿到苹果和吃掉苹果是连续同步执行,没有其他的线程干扰 方案一: 设置同步代码块(同步锁)。...
  • iOS解决多线程 线程阻塞问题

    千次阅读 2015-10-21 18:21:52
    一:这样处理之后不会影响外部视图的展示,亲测可行 #import "ThreadSDK.h" @interface ThreadSDK (){   BOOL threadDone; } @end @implementation ...- (void)thread {
  • 什么是多线程

    千次阅读 多人点赞 2018-06-28 13:32:36
    多线程
  • C++ 多线程编程解决CPU占用率高问题

    千次阅读 2014-06-30 23:11:40
    在用C++进行多线程编程的时候,时常需要写一些类似于死循环的结构来执行代码,这些死循环如果没有经过处理一直开着,那么CPU的占用率就会居高不下的做着很多无用的操做。为解决这个问题我们可以通过在各线程中的适当...
  • 万字图解Java多线程

    万次阅读 多人点赞 2020-09-06 14:45:07
    前言 java多线程我个人觉得是javaSe中最难的一部分,我以前也是感觉学会了,但是真正有多线程的需求却不知道怎么下手,...什么是java多线程? 进程与线程 进程 当一个程序被运行,就开启了一个进程, 比如启动了qq,w.
  • 多线程解决现实中的抢票问题 以下为两种解决方式: 1.实现Runnable接口 2.继承自Thread 1.第一种方法(正确)Public class A extends Thread{ Public static int tickets = 100; Public static String ...
  • 多线程解决for循环效率问题

    万次阅读 2018-03-16 11:28:19
    由打印结果可知:m1方法是用到了多线程的,多线程此时被线程池管理;而m2方法始终是main主线程执行的。 采用先把要执行的“耗时”内容放到一个线程的执行主体(run方法)里面,再用线程池执行该线程,可大大减少for...
  • 多线程竞争及解决方法

    千次阅读 2019-03-26 21:23:27
    解决多线程问题的方法:锁。 锁的好处:确保了某段关键代码(共享数据资源)只能有一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。 锁的坏处:阻止了多线程并发执行,包含锁的某段代码实际上只能...
  • 多线程临界区问题解决方法

    千次阅读 2016-08-08 13:15:44
    临界区定义:函数内同时运行线程...当线程对同一块内存空间进行操作时,就有可能产生临界区问题。下面先看一个例子:#include #include #include #include #define NUM_THREAD 100void *thread_inc(void *
  • 多线程时使用@Autowired总是获取不到bean,原因是:new thread不在spring容器中,也就无法获得spring中的bean对象。 解决方法:手动获取 package com.test.configs;   import org.springframework.beans....
  • Java 多线程并发解决方案

    千次阅读 2015-03-22 22:08:54
    Java 多线程并发编程会有许多不同的问题,主要有如下...synchronized关键字和Lock并发锁:主要解决多线程共享数据同步问题。 ThreadLocal主要解决多线程中数据因并发产生不一致问题。 ThreadLocal与synchroni
  • 解决python多线程的返回值问题

    万次阅读 2018-06-07 20:51:23
    前几天看了下python的多线程,但是发现创建的线程得不到函数的返回值,查阅资料发现需要重写Thread类,直接上代码。import threading import time """重新定义带返回值的线程类""" ...
  • 多线程并发同步问题解决方案

    千次阅读 2019-07-20 09:46:08
    线程并发同步 ...在多线程里面有些敏感数据不允许被多个线程同时访问,使用线程同步就是要保证在一个时间内只有一个线程访问一个数据,以此来保证数据的正确性。 线程同步中可能存在的安全隐患解决方法 1...
  • qt数据库多线程问题解决

    千次阅读 2015-01-24 15:46:01
    Qt数据库由QSqlDatabase::addDatabase()生成的QSqlDatabase只能在创建它的线程中使用, 在多线程中共用连接或者在另外一个线程中创建query都是不支持的 几乎国内没有文章提到这个问题,这几天在做数据库压力测试时...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 896,838
精华内容 358,735
关键字:

多线程是解决什么问题的