精华内容
下载资源
问答
  • 问:多线程是不是能加快处理速度?...假设我要拷贝100万条数据,单CPU电脑,用一个进程,在单线程的情况下,CPU占用率为5%,耗时1000秒。那么当在这个进程下,开辟10个线程同时去运行,是不是CPU占用...

    问:多线程是不是能加快处理速度?

    解析:

    在使用多线程时,一定要知道一个道理:处理速度的最终决定因素是CPU、内存等在单CPU(无论多少核)上,分配CPU资源的单位是“进程”而不是“线程”

    我们可以做一个简单的试验:

    假设我要拷贝100万条数据,单CPU电脑,用一个进程,在单线程的情况下,CPU占用率为5%,耗时1000秒。那么当在这个进程下,开辟10个线程同时去运行,是不是CPU占用率增加到50%,耗时减少到100秒呢?显然不是。我实测出来的情况是这样的:

    “CPU占用率仍然是5%,总耗时仍然是1000秒。且每个线程的运行时间也为1000秒。”

    重点是后面那句,怎么理解?意味着什么?

    我的理解如下:进程只有一个,所以分配的CPU资源是一定的,多线程只不过是轮流抢占CPU而已,并不会真正提高处理速度。这意味着,多线程的作用主要在于提高了并发数量,比如http请求,如果是单线程,一次只能接收一个请求,多线程则可以同时接收多个请求

    但是多线程由于轮换使用CPU,会造成单个线程的执行速度变慢(以前CPU供一个线程使用,现在要供多个线程轮流使用了)。但是在多CPU的服务器上,多线程就很有优势了,它不但能提高并发数量,而且能提高处理速度。因为在多CPU的服务器上,CPU调度很灵活,当一个线程占用着一个CPU的时候,其他线程可以被分配给其他CPU去处理,从而实现了“真正意义上地并行”。

     

    总结:

    第一,看硬件。如果是在比较强大的、多CPU的服务器上运行程序,可以使用多线程来提高并发数和执行速度但是线程也不宜过多,即使是16个CPU的服务器,同一时间最多也只能真正意义上地并发处理16个线程,多出来的线程还是要等待

    第二,看用途。如果你不在乎处理速度,仅仅是为了提高并发处理能力,那么理所当然地用多线程,但是如果你仅仅是想提高处理速度,且又是在单CPU机器上运行,那么多线程并不值得。如果你的任务很耗时,且可以一部分、一部分地做,那么最好不要用多线程(好比搬砖,单线程一次搬10块,总共搬10天,但搬一块算一块,到第9天的时候,你就搬完90块砖了;如果你用10个线程同时去搬砖,同样要搬10天,但是到第9天的时候,这10个线程100块砖都“还在路上”,一块砖都没搬完!)。

     

    一个实际例子

     

    在单线程的情况下,假如说我们生成10个报表文件需要1个小时。

     

    如果是在多线程的情况下呢,生成10个文件要多少个小时?

    同样要1个小时。不管你有多少个线程,就算你有100个线程,一样需要1个小时。

     

    这就是问题所在。单线程和多线程的区别在哪里?

    单线程是先执行完第一个报表,用了6分钟,再执行第二个报表,也用6分钟。

    多线程,是10个报表一起执行,但是每一个报表都要1个小时。

     

    同一个线程,比如说一个servlet,一个人去访问,执行它,只需要2秒。

     

    如果两个人同时去访问,可能就要4秒。如果10个人同时去访问,那么每个人就要

    等待20秒。就是说它基本上是一个成倍的线性增长。

     

    一个线程占2%的CPU,那是不是50个线程就占100%的CPU?不是。

    50个线程仍然占2%的CPU,分配CPU资源的单位是进程,而不是线程。

    所以说线程是无法加快执行速度的

    多线程的总体执行时间和单线程是一样的,但是多线程单个线程的执行时间

    是单线程的多倍。

    我这里有一组数据,我有两个任务,如果是单线程。

    先执行任务1,再执行任务2,一共用了80秒钟,每个任务花费40秒

    如果我用2个线程同时跑,一共也用了80秒,但是每个任务都花费80秒(也就是说在最后一刻的时候,两个任务几乎是同时完成)。

     

    附:以下两组数据是单线程和多线程耗时的对比(win7系统、笔记本i7 CPU)。

     

     单位:毫秒

    编号

    单线程1

    单线程2

    2个线程同时运行

    测试1

    81284

    81020

    155373

    测试2

    79601

    78405

    154144

     

    从上面数据可见,2个线程同时运行的时间,几乎等于线程1+线程2单独运行的时间。所以说多线程实际上总时间没变,但是单个任务的执行时间延长了。

    我的测试:

    463600条数据模拟银行账户转账:

    线程数 结果 时间
    1

    8118ms
    100

    15011ms
    200

    12665ms

    400

    13533ms

    使用的数据:10 25 2500:表示将2500元从账号10转到了账号25。

    代码 下载地址:https://download.csdn.net/download/qq_36396104/10852122

    展开全文
  • 程序在执行的过程中消耗的是cpu,比如只有一个单核cpu,多个线程同时执行工作时,需要不断切换执行,这就是线程的上下文切换,时间耗费更多,而单线程只是一个线程再跑。 多线程提高的是并发数量,执行的是不变的,...

    前言:

    面试官:多线程和单线程哪个快?
    我凭借微弱的记忆以及正经分析回答:多线程快,因为可以执行多个任务,而单线程只能执行一个任务。
    面试官:无论什么情况多线程都比单线程快吗?
    我犹豫了一下正经回答:不是的,线程执行消耗的是cpu的,cpu资源是有限的,所有不是一直比单线程快。
    面试官:那什么情况下单线程比多线程快?
    我…:这…不清楚。
    面试官:你没有学过《计算机基础》吗?你已经自相矛盾了…
    我:没学过,好吧,我不知道。
    后来这个面试官变成了我的老大,技术总监,进公司第一天下午讨论某问题之后单独叫我谈话。
    老大问我那个问题有没有去了解过。
    我虚了:我看了,不一定,多线程存在上下文切换。
    老大:多线程一定比单线程慢,上下文切换你知道什么意思吗?
    我心想完了:额…看了忘记了。
    然后噼里啪啦跟我解释,我现在依旧忘记了…

    正文:

    回家后我再次查阅资料:

    首先分配cpu资源的单位是进程。一个进程所获得到的cpu资源是一定的。程序在执行的过程中消耗的是cpu,比如一个单核cpu,多个线程同时执行工作时,需要不断切换执行(上下文切换),单个线程时间耗费更多了,而单线程只是一个线程跑。
    比如:
    处理10个报表,
    在单线程的情况下,假如说我们生成10个报表文件需要1个小时。
    单线程是先执行完第一个报表,用了6分钟,再执行第二个报表,也用6分钟。总计1个小时
    多线程,是10个报表一起执行,但是每一个报表都要1个小时。
    总结:多线程的总体执行时间和单线程是一样的,但是多线程中单个线程的执行时间是单线程的多倍。
    多线程提高的是并发数量,比如现在有一个4核cpu的服务器,同一时间可执行4个线程,这样处理线程任务的速度比较快。但是多出来的线程,5个,6个,7个,多出的线程还是要等待。

    上下文切换
    多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。

    概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换会这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。

    后记:

    基础不牢固的情况下去回答问题是非常容易打脸的,回答出的答案也是稀里糊涂的,稍微换个角度攻击就会一脸懵逼,学习更多的新技术其实没太大用处,因为这个行业技术迭代的很快,基础才是永久的饭碗,所以我决定从java虚拟机开始入手,修炼内功,找对方向获得会更多!

    展开全文
  • “多个人干活一个人干活要快,多线程并行执行也比单线程要快”这是我学习编程长期以来的想法。然而在实际的开发过程中,并不是所有情况下都是这样。先看看下面的程序(点击下载): ThreadTester是所有Tester的...

    2014-05-04 07:56:50cnblogs.com-Ethan Cai-点击数: 306

    “多个人干活比一个人干活要快,多线程并行执行也比单线程要快”这是我学习编程长期以来的想法。然而在实际的开发过程中,并不是所有情况下都是这样。先看看下面的程序(点击下载):

    ThreadTester是所有Tester的基类。所有的Tester都干的是同样一件事情,把counter增加到100000000,每次只能加1。

    public abstract class ThreadTester {
        public const long MAX_COUNTER_NUMBER = 100000000;
        private long _counter = 0;
    
        //获得计数
        public long GetCounter() {
            return this._counter;
        }
        //增加计数器
        protected  void IncreaseCounter() {
            this._counter += 1;
        }
        //启动测试
        public abstract void Start();
    
        //获得Counter从开始增加到现在的数字所耗的时间
        public abstract long GetElapsedMillisecondsOfIncreaseCounter();
        
        //测试是否正在运行
        public abstract bool IsTesterRunning();
    }

    SingleThreadTester是单线程计数。

    class SingleThreadTester :ThreadTester
    {
        private Stopwatch _aStopWatch=new Stopwatch();
        
        public override void Start()
        {
            _aStopWatch.Start();
            Thread aThread=new Thread(()=>WorkInThread());
            aThread.Start();
        }
        public override long GetElapsedMillisecondsOfIncreaseCounter()
        {
             return this._aStopWatch.ElapsedMilliseconds;
        }
    
        public override bool IsTesterRunning()
        {
             return _aStopWatch.IsRunning;
        }
    
        private void WorkInThread()
        {
            while(true)
            {
                if(this.GetCounter()>ThreadTester.MAX_COUNTER_NUMBER)
                {
                    _aStopWatch.Stop();
                    break;
                }
                this.IncreaseCounter();
            }
        }
    }


    TwoThreadSwitchTester是两个线程交替计数。

    class TwoThreadSwitchTester : ThreadTester
    {
        private Stopwatch _aStopWatch = new Stopwatch();
        private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
        
        public override void Start()
        {
            _aStopWatch.Start();
    
            Thread aThread1 = new Thread(() => Work1InThread());
            aThread1.Start();
            
            Thread aThread2 = new Thread(() => Work2InThread());
            aThread2.Start();
        }
    
        public override long GetElapsedMillisecondsOfIncreaseCounter()
        {
            return this._aStopWatch.ElapsedMilliseconds;
        }
         
        public override bool IsTesterRunning()
        {
            return _aStopWatch.IsRunning;
        }
         
        private void Work1InThread()
        {
            while (true)
            {
                _autoResetEvent.WaitOne();
                this.IncreaseCounter();
                if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
                {
                    _aStopWatch.Stop();
                    break;
                }
                _autoResetEvent.Set();
            }
        }
    
        private void Work2InThread()
        {
            while (true)
            {
                _autoResetEvent.Set();
                _autoResetEvent.WaitOne();
                this.IncreaseCounter();
    
                if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
                {
                    _aStopWatch.Stop();
                    break;
                }
            }
        }
    }


    MultiThreadTester可以指定线程数,多个线程争抢计数。

    class MultiThreadTester : ThreadTester
    {
        private Stopwatch _aStopWatch = new Stopwatch();
        private readonly int _threadCount = 0;
        private readonly object _counterLock = newobject();
    
        public MultiThreadTester(int threadCount)
        {
            this._threadCount = threadCount;
        }
    
        public override void Start()
        {
            _aStopWatch.Start();
            for (int i = 0; i < _threadCount; i++)
            {
                Thread aThread = new Thread(() => WorkInThread());
                aThread.Start();
            }
        }
    
        public override long GetElapsedMillisecondsOfIncreaseCounter()
        {
            return this._aStopWatch.ElapsedMilliseconds;
        }
    
        public override bool IsTesterRunning()
        {
            return _aStopWatch.IsRunning;
        }
         
        private void WorkInThread()
        {
            while (true)
            {
                lock (_counterLock)
                {
                    if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
                    {
                        _aStopWatch.Stop();
                        break;
                    }
                     
                    this.IncreaseCounter();
                }
            }
        }
    }


    Program的Main函数中,根据用户的选择来决定执行哪个测试类。

    import java.io.Console;
    
    class Program
    {
        static void Main(string[] args)
        {
    
            string inputText = GetUserChoice();
    
            while (!"4".Equals(inputText))
            {
                ThreadTester tester = GreateThreadTesterByInputText(inputText);
                tester.Start();
     
                while (true)
                {
                    Console.WriteLine(GetStatusOfThreadTester(tester));
                    if (!tester.IsTesterRunning())
                    {
                        break;
                    }
                    Thread.Sleep(100);
                }
     
                inputText = GetUserChoice();
            }
     
            Console.Write("Click enter to exit...");
        }
     
        private static string GetStatusOfThreadTester(ThreadTester tester)
        {
            return string.Format("[耗时{0}ms] counter = {1}, {2}",
                    tester.GetElapsedMillisecondsOfIncreaseCounter(), tester.GetCounter(),
                    tester.IsTesterRunning() ? "running" : "stopped");
        }
     
        private static ThreadTester GreateThreadTesterByInputText(string inputText)
    {
        switch (inputText)
        {
            case"1":
                return new SingleThreadTester();
            case"2":
                return new TwoThreadSwitchTester();
            default:
                return new MultiThreadTester(100);
        }
    }
     
        private static string GetUserChoice()
        {
            Console.WriteLine(@"==Please select the option in the following list:==
            1. SingleThreadTester
            2. TwoThreadSwitchTester
            3. MultiThreadTester
            4. Exit");
     
            string inputText = Console.ReadLine();
     
            return inputText;
        }
    }


    三个测试类,运行结果如下:

    Single Thread:
    [耗时407ms] counter = 100000001, stopped
    [耗时453ms] counter = 100000001, stopped
    [耗时412ms] counter = 100000001, stopped

    Two Thread Switch:
    [耗时161503ms] counter = 100000001, stopped
    [耗时164508ms] counter = 100000001, stopped
    [耗时164201ms] counter = 100000001, stopped

    Multi Threads - 100 Threads:
    [耗时3659ms] counter = 100000001, stopped
    [耗时3950ms] counter = 100000001, stopped
    [耗时3720ms] counter = 100000001, stopped

    Multi Threads - 2 Threads:
    [耗时3078ms] counter = 100000001, stopped
    [耗时3160ms] counter = 100000001, stopped
    [耗时3106ms] counter = 100000001, stopped

    什么是线程上下文切换

    上下文切换的精确定义可以参考: http://www.linfo.org/context_switch.html。多任务系统往往需要同时执行多道作业。作业数往往大于机器的CPU数,然而一颗CPU同时只能执行一项任务,为了让用户感觉这些任务正在同时进行,操作系统的设计者巧妙地利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能,但同时也带来了保存现场和加载现场的直接消耗。(Note. 更精确地说, 上下文切换会带来直接和间接两种因素影响程序性能的消耗. 直接消耗包括: CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例需要重新加载, CPU 的pipeline需要刷掉; 间接消耗指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小).

    根据上面上下文切换的定义,我们做出下面的假设:

    1. 之所以TwoThreadSwitchTester执行速度最慢,因为线程上下文切换的次数最多,时间主要消耗在上下文切换了,两个线程交替计数,每计数一次就要做一次线程切换。
    2. “Multi Threads - 100 Threads”比“Multi Threads - 2 Threads”开的线程数量要多,导致线程切换次数也比后者多,执行时间也比后者长。

    由于Windows下没有像Linux下的vmstat这样的工具,这里我们使用Process Explorer看看程序执行的时候线程上线文切换的次数。

    Single Thread:

    计数期间,线程总共切换了580-548=32次。(548是启动程序后,初始的数值)

    Two Thread Switch:

    计数期间,线程总共切换了33673295-124=33673171次。(124是启动程序后,初始的数值)

    Multi Threads - 100 Threads:

    计数期间,线程总共切换了846-329=517次。(329是启动程序后,初始的数值)

    Multi Threads - 2 Threads:

    计数期间,线程总共切换了295-201=94次。(201是启动程序后,初始的数值)

    从上面收集的数据来看,和我们的判断基本相符。

    干活的其实是CPU,而不是线程

    再想想原来学过的知识,之前一直以为线程多干活就快,简直是把学过的计算机原理都还给老师了。真正干活的不是线程,而是CPU。线程越多,干活不一定越快。

    那么高并发的情况下什么时候适合单线程,什么时候适合多线程呢?

    适合单线程的场景:单个线程的工作逻辑简单,而且速度非常快,比如从内存中读取某个值,或者从Hash表根据key获得某个value。Redis和Node.js这类程序都是单线程,适合单个线程简单快速的场景。

    适合多线程的场景:单个线程的工作逻辑复杂,等待时间较长或者需要消耗大量系统运算资源,比如需要从多个远程服务获得数据并计算,或者图像处理。

    例子程序:http://pan.baidu.com/s/1c05WrGO

    参考:

    • Context Switch – Wikipedia
    • 多线程的代价
    • Threading in C#
    • 为什么我要用 Node.js? 案例逐一介绍
    • 知乎——redis是个单线程的程序,为什么会这么快呢?每秒10000?这个有点不解,具体是快在哪里呢?EPOLL?内存?多线程
    • 从Java视角理解系统结构(一)CPU上下文切换

     

    如下是自己写的多线程验证代码:

    1.Junit测试类
    
    import java.io.IOException;
    
    import java.io.InputStream;
    
    import java.net.SocketException;
    
    import java.util.ArrayList;
    
    import org.apache.commons.net.ftp.FTPClient;
    
    import org.junit.Test;
    
    import com.Thread.MyThread;
    
    public class MainTest {
    
     private int count = 10000;
    
     private int countTest = 10000;
    
      //用主线程遍历ArrayList(结论:如果循环中不sleep,速度最快,如果循环中sleep,则遍历速度比多线程慢)
    
     @Test
    
     public void test() throws InterruptedException {
    
      long startTime = System.currentTimeMillis();
    
      ArrayList<Integer>  testList = new ArrayList<Integer>();
    
      ArrayList<Integer>  testList1 = new ArrayList<Integer>();
    
      for(int i=0; i<count; i++){
    
       testList.add(i);
    
      }
    
      for(int j=0; j<countTest; j++){
    
       testList1.add(j);
    
      }
    
    
    
      System.out.println("testList.size():" + testList.size() + "   i=" + 0);
    
    
    
      for(int j=0; j<testList.size(); j++){
    
       Thread.sleep(2);
    
       System.out.println(Thread.currentThread().getName() + " :" + testList.get(j));
    
      }
    
    
    
      for(int jj=0; jj<testList1.size(); jj++){
    
       Thread.sleep(2);
    
       System.out.println(Thread.currentThread().getName() + " :" + testList.get(jj));
    
      }
    
    
    
    
    
      System.out.println("程序总共花费时间:" + (System.currentTimeMillis()-startTime) + "毫秒");
    
     }
    
    
    
    //在主线程中启动一个子线程去遍历ArrayList(结论:在循环中如果不sleep,遍历速度仅次于主线程,如果循环中sleep,速度与主线程遍历差不多)
    
     @Test
    
     public void test0() {
    
      long startTime = System.currentTimeMillis();
    
      ArrayList<Integer>  testList = new ArrayList<Integer>();
    
      ArrayList<Integer>  testList1 = new ArrayList<Integer>();
    
      for(int j=0; j<countTest; j++){
    
       testList1.add(j);
    
      }
    
      for(int i=0; i<count; i++){
    
       testList.add(i);
    
      }
    
      System.out.println("testList.size():" + testList.size() + "   i=" + 0);
    
    
    
      Runnable run0 = new MyThread(testList,0, count);
    
      Thread t0 = new Thread(run0, "Thread-0");
    
      long startTime1 = System.currentTimeMillis();
    
      t0.start();
    
      System.out.println("线程启动时间:" + (System.currentTimeMillis() - startTime1));
    
      try {
    
       t0.join();
    
      } catch (InterruptedException e) {
    
       e.printStackTrace();
    
      }
    
      System.out.println("程序总共花费时间:" + (System.currentTimeMillis()-startTime) + "毫秒");
    
     }
    
      //在主线程中启动两个子线程去遍历ArrayList(结论:如果在循环中不sleep,速度最慢,如果在循环中sleep,则遍历速度最快)
    
     @Test
    
     public void test1() {
    
      long startTime = System.currentTimeMillis();
    
      ArrayList<Integer>  testList = new ArrayList<Integer>();
    
      ArrayList<Integer>  testList1 = new ArrayList<Integer>();
    
    
    
      for(int i=0; i<count; i++){
    
       testList.add(i);
    
      }
    
    
    
      for(int j=0; j<countTest; j++){
    
       testList1.add(j);
    
      }
    
      System.out.println("testList.size():" + testList.size() + "   i=" + 0);
    
    
    
      Runnable run1 = new MyThread(testList,0, count/2);
    
      Thread t1 = new Thread(run1, "Thread-1");
    
      t1.start();
    
    
    
      Runnable run2 = new MyThread(testList,(count/2+1), count);
    
      Thread t2 = new Thread(run2, "Thread-2");
    
      t2.start();
    
      try {
    
       t1.join();
    
       t2.join();
    
      } catch (InterruptedException e) {
    
       e.printStackTrace();
    
      }
    
      System.out.println("程序总共花费时间:" + (System.currentTimeMillis()-startTime) + "毫秒");
    
     }
    
      //用单线程从ftp上下载两个文件
    
     @Test
    
     public void FileUpload() {
    
      long startTime = System.currentTimeMillis();
    
      try {
    
       FTPClient ftp = new FTPClient();
    
       ftp.connect("192.168.173.156", 21);
    
       ftp.login("admin", "123456");
    
       InputStream in = ftp.retrieveFileStream("ax.java");
    
       byte[] buffer = new byte[1024];
    
       while((in.read(buffer))!=-1){
    
        //System.out.println(Arrays.toString(buffer));
    
       }
    
       in.close();
    
       
    
       FTPClient ftp1 = new FTPClient();
    
       ftp1.connect("192.168.173.156", 21);
    
       ftp1.login("admin", "123456");
    
       InputStream in1 = ftp1.retrieveFileStream("ay.java");
    
       byte[] buffer1 = new byte[1024];
    
       while((in1.read(buffer1))!=-1){
    
        //System.out.println(Arrays.toString(buffer));
    
       }
    
       in1.close();
    
    
    
       
    
      } catch (SocketException e) {
    
       // TODO Auto-generated catch block
    
       e.printStackTrace();
    
      } catch (IOException e) {
    
       // TODO Auto-generated catch block
    
       e.printStackTrace();
    
      }
    
    
    
      System.out.println("程序总共花费时间:" + (System.currentTimeMillis()-startTime) + "毫秒");
    
     }
    
      //用两个线程从ftp上下载两个文件(结论:是多线程下载速度比单线程下载速度快)
    
     @Test
    
     public void FileUpload1() {
    
      long startTime = System.currentTimeMillis();
    
    
    
     /* Runnable run1 = new MyThread("ax.java");
    
      Thread t1 = new Thread(run1, "Thread-1");
    
      t1.start();
    
    
    
      Runnable run2 = new MyThread("ay.java");
    
      Thread t2 = new Thread(run2, "Thread-2");
    
      t2.start();
    
    
    
      try {
    
       t1.join();
    
       t2.join();
    
      } catch (InterruptedException e) {
    
       e.printStackTrace();
    
      }
    
      System.out.println("程序总共花费时间:" + (System.currentTimeMillis()-startTime) + "毫秒");*/
    
     }
    
    }

     

    2、多线程实现类

    package com.Thread;
    
    import java.io.IOException;
    
    import java.io.InputStream;
    
    import java.net.SocketException;
    
    import java.util.ArrayList;
    
    import java.util.Arrays;
    
    import org.apache.commons.net.ftp.FTPClient;
    
    public class MyThread implements Runnable{
    
     private ArrayList<Integer> intList = new ArrayList<Integer>();
    
     private int startNum;
    
     private int endNum;
    
     private String filename;
    
    
    
     public MyThread(ArrayList<Integer> intList, int startNum, int endNum) {
    
      super();
    
      this.intList = intList;
    
      this.startNum = startNum;
    
      this.endNum = endNum;
    
     }
    
    
    
     @Override
    
     public void run() {
    
      System.out.println("线程"+Thread.currentThread().getName()+"开始启动……");
    
      System.out.println("intList.size():" + intList.size() + "   startNum=" + startNum + "    endNum=" + endNum);
    
      for(int m=startNum; m<endNum;m++){
    
       System.out.println(Thread.currentThread().getName() + " : " + intList.get(m));
    
       try {
    
        Thread.sleep(2);
    
       } catch (InterruptedException e) {
    
        // TODO Auto-generated catch block
    
        e.printStackTrace();
    
       }
    
      }
    
      System.out.println("线程"+Thread.currentThread().getName()+"执行结束……");
    
    
    
      /*try {
    
       FTPClient ftp = new FTPClient();
    
       ftp.connect("192.168.173.156", 21);
    
       ftp.login("admin", "123456");
    
       InputStream in = ftp.retrieveFileStream(filename);
    
       byte[] buffer = new byte[1024];
    
       while((in.read(buffer))!=-1){
    
        //System.out.println(Arrays.toString(buffer));
    
       }
    
       in.close();*/
    
     }
    
    
    
     /*public MyThread(String filename) {
    
      super();
    
      this.filename = filename;
    
     }*/
    
    }
    
    

    请关注我微信公众号:

    展开全文
  • 在Windows编程中,多线程一定比单线程快吗?什么时候该用多线程?什么时候该用单线程?它们各自的应用场景是什么?
  • “多个人干活一个人干活要快,多线程并行执行也比单线程要快”这是我学习编程长期以来的想法。然而在实际的开发过程中,并不是所有情况下都是这样。先看看下面的程序(点击下载): ThreadTester是所有...
    
    

    “多个人干活比一个人干活要快,多线程并行执行也比单线程要快”这是我学习编程长期以来的想法。然而在实际的开发过程中,并不是所有情况下都是这样。先看看下面的程序(点击下载):

    image

    ThreadTester是所有Tester的基类。所有的Tester都干的是同样一件事情,把counter增加到100000000,每次只能加1。

       1: public abstract class ThreadTester
       2:     {
       3:         public const long MAX_COUNTER_NUMBER = 100000000;
       4:  
       5:         private long _counter = 0;
       6:  
       7:         //获得计数
       8:         public virtual long GetCounter()
       9:         {
      10:             return this._counter;
      11:         }
      12:  
      13:         //增加计数器
      14:         protected virtual void IncreaseCounter()
      15:         {
      16:             this._counter += 1;
      17:         }
      18:  
      19:         //启动测试
      20:         public abstract void Start();
      21:  
      22:         //获得Counter从开始增加到现在的数字所耗的时间
      23:         public abstract long GetElapsedMillisecondsOfIncreaseCounter();
      24:  
      25:         //测试是否正在运行
      26:         public abstract bool IsTesterRunning();
      27:     }

    SingleThreadTester是单线程计数。

       1: class SingleThreadTester : ThreadTester
       2:     {
       3:         private Stopwatch _aStopWatch = new Stopwatch();
       4:  
       5:         public override void Start()
       6:         {
       7:             _aStopWatch.Start();
       8:  
       9:             Thread aThread = new Thread(() => WorkInThread());
      10:             aThread.Start();
      11:         }
      12:  
      13:         public override long GetElapsedMillisecondsOfIncreaseCounter()
      14:         {
      15:             return this._aStopWatch.ElapsedMilliseconds;
      16:         }
      17:  
      18:         public override bool IsTesterRunning()
      19:         {
      20:             return _aStopWatch.IsRunning;
      21:         }
      22:  
      23:         private void WorkInThread()
      24:         {
      25:             while (true)
      26:             {
      27:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
      28:                 {
      29:                     _aStopWatch.Stop();
      30:                     break;
      31:                 }
      32:  
      33:                 this.IncreaseCounter();
      34:             }
      35:         }
      36:     }

    TwoThreadSwitchTester是两个线程交替计数。

       1: class TwoThreadSwitchTester : ThreadTester
       2:     {
       3:         private Stopwatch _aStopWatch = new Stopwatch();
       4:         private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
       5:  
       6:         public override void Start()
       7:         {
       8:             _aStopWatch.Start();
       9:  
      10:             Thread aThread1 = new Thread(() => Work1InThread());
      11:             aThread1.Start();
      12:  
      13:             Thread aThread2 = new Thread(() => Work2InThread());
      14:             aThread2.Start();
      15:         }
      16:  
      17:         public override long GetElapsedMillisecondsOfIncreaseCounter()
      18:         {
      19:             return this._aStopWatch.ElapsedMilliseconds;
      20:         }
      21:  
      22:         public override bool IsTesterRunning()
      23:         {
      24:             return _aStopWatch.IsRunning;
      25:         }
      26:  
      27:         private void Work1InThread()
      28:         {
      29:             while (true)
      30:             {
      31:                 _autoResetEvent.WaitOne();
      32:                 
      33:                 this.IncreaseCounter();
      34:  
      35:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
      36:                 {
      37:                     _aStopWatch.Stop();
      38:                     break;
      39:                 }
      40:  
      41:                 _autoResetEvent.Set();
      42:             }
      43:         }
      44:  
      45:         private void Work2InThread()
      46:         {
      47:             while (true)
      48:             {
      49:                 _autoResetEvent.Set();
      50:                 _autoResetEvent.WaitOne();
      51:                 this.IncreaseCounter();
      52:  
      53:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
      54:                 {
      55:                     _aStopWatch.Stop();
      56:                     break;
      57:                 }
      58:             }
      59:         }
      60:     }

    MultiThreadTester可以指定线程数,多个线程争抢计数。

       1: class MultiThreadTester : ThreadTester
       2:     {
       3:         private Stopwatch _aStopWatch = new Stopwatch();
       4:         private readonly int _threadCount = 0;
       5:         private readonly object _counterLock = new object();
       6:         
       7:         public MultiThreadTester(int threadCount)
       8:         {
       9:             this._threadCount = threadCount;
      10:         }
      11:  
      12:         public override void Start()
      13:         {
      14:             _aStopWatch.Start();
      15:  
      16:             for (int i = 0; i < _threadCount; i++)
      17:             {
      18:                 Thread aThread = new Thread(() => WorkInThread());
      19:                 aThread.Start();
      20:             }
      21:         }
      22:  
      23:         public override long GetElapsedMillisecondsOfIncreaseCounter()
      24:         {
      25:             return this._aStopWatch.ElapsedMilliseconds;
      26:         }
      27:  
      28:         public override bool IsTesterRunning()
      29:         {
      30:             return _aStopWatch.IsRunning;
      31:         }
      32:  
      33:         private void WorkInThread()
      34:         {
      35:             while (true)
      36:             {
      37:                 lock (_counterLock)
      38:                 {
      39:                     if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
      40:                     {
      41:                         _aStopWatch.Stop();
      42:                         break;
      43:                     }
      44:  
      45:                     this.IncreaseCounter();
      46:                 }
      47:             }
      48:         }
      49:     }

    Program的Main函数中,根据用户的选择来决定执行哪个测试类。

       1: class Program
       2:     {
       3:         static void Main(string[] args)
       4:         {
       5:  
       6:             string inputText = GetUserChoice();
       7:  
       8:             while (!"4".Equals(inputText))
       9:             {
      10:                 ThreadTester tester = GreateThreadTesterByInputText(inputText);
      11:                 tester.Start();
      12:  
      13:                 while (true)
      14:                 {
      15:                     Console.WriteLine(GetStatusOfThreadTester(tester));
      16:                     if (!tester.IsTesterRunning())
      17:                     {
      18:                         break;
      19:                     }
      20:                     Thread.Sleep(100);
      21:                 }
      22:  
      23:                 inputText = GetUserChoice();
      24:             }
      25:  
      26:             Console.Write("Click enter to exit...");
      27:         }
      28:  
      29:         private static string GetStatusOfThreadTester(ThreadTester tester)
      30:         {
      31:             return string.Format("[耗时{0}ms] counter = {1}, {2}",
      32:                     tester.GetElapsedMillisecondsOfIncreaseCounter(), tester.GetCounter(),
      33:                     tester.IsTesterRunning() ? "running" : "stopped");
      34:         }
      35:  
      36:         private static ThreadTester GreateThreadTesterByInputText(string inputText)
      37:         {
      38:             switch (inputText)
      39:             {
      40:                 case "1":
      41:                     return new SingleThreadTester();
      42:                 case "2":
      43:                     return new TwoThreadSwitchTester();
      44:                 default:
      45:                     return new MultiThreadTester(100);
      46:             }
      47:         }
      48:  
      49:         private static string GetUserChoice()
      50:         {
      51:             Console.WriteLine(@"==Please select the option in the following list:==
      52: 1. SingleThreadTester
      53: 2. TwoThreadSwitchTester
      54: 3. MultiThreadTester
      55: 4. Exit");
      56:  
      57:             string inputText = Console.ReadLine();
      58:  
      59:             return inputText;
      60:         }
      61:     }

    三个测试类,运行结果如下:

    Single Thread:
    [耗时407ms] counter = 100000001, stopped
    [耗时453ms] counter = 100000001, stopped
    [耗时412ms] counter = 100000001, stopped

    Two Thread Switch:
    [耗时161503ms] counter = 100000001, stopped
    [耗时164508ms] counter = 100000001, stopped
    [耗时164201ms] counter = 100000001, stopped

    Multi Threads - 100 Threads:
    [耗时3659ms] counter = 100000001, stopped
    [耗时3950ms] counter = 100000001, stopped
    [耗时3720ms] counter = 100000001, stopped

    Multi Threads - 2 Threads:
    [耗时3078ms] counter = 100000001, stopped
    [耗时3160ms] counter = 100000001, stopped
    [耗时3106ms] counter = 100000001, stopped

    什么是线程上下文切换

    上下文切换的精确定义可以参考: http://www.linfo.org/context_switch.html。多任务系统往往需要同时执行多道作业。作业数往往大于机器的CPU数,然而一颗CPU同时只能执行一项任务,为了让用户感觉这些任务正在同时进行,操作系统的设计者巧妙地利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能,但同时也带来了保存现场和加载现场的直接消耗。(Note. 更精确地说, 上下文切换会带来直接和间接两种因素影响程序性能的消耗. 直接消耗包括: CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例需要重新加载, CPU 的pipeline需要刷掉; 间接消耗指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小).

    context-switch

    根据上面上下文切换的定义,我们做出下面的假设:

    1. 之所以TwoThreadSwitchTester执行速度最慢,因为线程上下文切换的次数最多,时间主要消耗在上下文切换了,两个线程交替计数,每计数一次就要做一次线程切换。
    2. “Multi Threads - 100 Threads”比“Multi Threads - 2 Threads”开的线程数量要多,导致线程切换次数也比后者多,执行时间也比后者长。

    由于Windows下没有像Linux下的vmstat这样的工具,这里我们使用Process Explorer看看程序执行的时候线程上线文切换的次数。

    Single Thread:

    image

    计数期间,线程总共切换了580-548=32次。(548是启动程序后,初始的数值)

    Two Thread Switch:

    image

    计数期间,线程总共切换了33673295-124=33673171次。(124是启动程序后,初始的数值)

    Multi Threads - 100 Threads:

    image

    计数期间,线程总共切换了846-329=517次。(329是启动程序后,初始的数值)

    Multi Threads - 2 Threads:

    image

    计数期间,线程总共切换了295-201=94次。(201是启动程序后,初始的数值)

    从上面收集的数据来看,和我们的判断基本相符。

    干活的其实是CPU,而不是线程

    再想想原来学过的知识,之前一直以为线程多干活就快,简直是把学过的计算机原理都还给老师了。真正干活的不是线程,而是CPU。线程越多,干活不一定越快。

    那么高并发的情况下什么时候适合单线程,什么时候适合多线程呢?

    适合单线程的场景:单个线程的工作逻辑简单,而且速度非常快,比如从内存中读取某个值,或者从Hash表根据key获得某个value。Redis和Node.js这类程序都是单线程,适合单个线程简单快速的场景。

    适合多线程的场景:单个线程的工作逻辑复杂,等待时间较长或者需要消耗大量系统运算资源,比如需要从多个远程服务获得数据并计算,或者图像处理。

    例子程序:http://pan.baidu.com/s/1c05WrGO

    参考:

    • Context Switch – Wikipedia
    • 多线程的代价
    • Threading in C#
    • 为什么我要用 Node.js? 案例逐一介绍
    • 知乎——redis是个单线程的程序,为什么会这么快呢?每秒10000?这个有点不解,具体是快在哪里呢?EPOLL?内存?多线程
    展开全文
  • 2.多线程会存在线程上下文切换,会导致程序执行速度变,即采用一个拥有两个线程的进程执行所需要的时间一个线程的进程执行两次所需要的时间要多一些。 结论:即采用多线程不会提高程序的执行速度,反而会降低...
  • 多线程并发一定比单线程快吗?

    千次阅读 2019-08-02 19:19:54
    确实多线程在一定情况下比单线程更快。 下面的代码演示串行和并发执行并累加操作的时间,请分析:当count的数量增加 1万 -> 10万 -> 100万 -> 1000万 -> 1亿,下面的代码并发执行一定串行执行快吗?...
  • 1、多线程的产生并不是因为发明了多核...2、实际上,多线程的出现主要为了解决IO设备的读写速度往往CPU的处理速度造成的单线程程序运行阻塞问题,一个极端的例子就是如果你需要用户在键盘上输入一个数据,当用户没
  • 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。...多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务, 也...
  • 浏览器多线程和js单线程

    万次阅读 2017-08-02 15:13:11
    这些问题的根源就是因为浏览器的多线程和js的单线程引起的。看本篇博客之前,应该充分理解消息队列,事件循环,同步异步任务等概念。 这些概念以前都知道,也了解多线程的概念。但是当遇到问题的时候,这些东西都被...
  • 对于单核CPU下多线程程序在同一时间点都只能有一个线程运行,对于多核CPU可以实现真正的并发同步运行,这种说法正确吗? 另外在多线程的情况下使用互斥对象来实现线程同步,这样的话多线程程序的运行效率受影响吗?
  • 单线程拷贝一个文件和多线程拷贝一个文件,那个效率更高,求大神写个例子证明一下。
  • JavaScript是多线程还是单线程

    万次阅读 多人点赞 2018-04-19 16:54:28
    那么JavaScript是单线程还是多线程?通过查资料总结了JavaScript运行的原理。如下:一、为什么JavaScript是单线程?JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript...
  • python的多线程单线程的效率问题

    千次阅读 2018-11-01 15:54:29
    先了解下CPU的简单运行原理:  它运行速度非常快,1s内可以运行成千上万次,一个核心可以把1s切分成成千上万个时间... 再了解下单线程和多线程的区别:  先看下单进程,顾名思义,就是一条进程,类似于单向公路...
  • 1.js中多线程-单线程, 同步-异步,阻塞-非阻塞, 回调函数的关系理解 //2018.12.12 1.多线程/单线程 简单理解为: 多线程:程序可以同一时间做几件事. 单线程:程序同一时间只能做一件事. 在...
  • python 单线程多线程爬虫

    千次阅读 2017-10-22 15:09:15
    帮别人写爬虫,先是单线程,太,改了多线程 1.单线程 import urllib import urllib.request import requests import xlwt import re import string def set_style(name,height,bold=False): style = xlwt....
  • 多线程就一定比单线程快吗?

    千次阅读 2019-06-24 16:55:00
    为什么要使用多线程?   在探讨文章题目之前,我们先来思考这样一个问题,你为什么要使用多线程?我相信很多人在遇到这个问题时会不假思索的回答出答案,因为并发快,那为什么并发快呢? 对于多核 CPU 来说,每...
  • 大文件上传-单线程多线程

    千次阅读 2017-06-10 15:32:19
    大文件上传-单线程多线程
  • 多线程运行的原理 cpu在线程中做时间片的切换。 其实真正电脑中的程序的运行不是同时在运行的。CPU负责程序的运行,而CPU在运行程序的过程中某个时刻点上,它其实只能运行一个程序。而不是多个程序。而CPU它可以在...
  •  多线程和多进程是什么自行google补脑 ... 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂。...单线程    在好些年前的MS-DOS时代,操作系统处理问题都
  • 单线程多线程的区别

    万次阅读 2017-11-27 11:03:18
    什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括...而一个进程又是由线程所组成的。 什么是线程线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区
  • Python3 多线程为啥比单线程优秀

    千次阅读 2018-04-15 12:00:20
    先了解下CPU的简单运行原理:  它运行速度非常快,1s内可以运行成千上万次,一个核心可以把1s切分成成千上万个时间... 再了解下单线程和多线程的区别:  先看下单进程,顾名思义,就是一条进程,类似于单向公路...
  • Redis单线程查询

    千次阅读 2019-02-26 13:19:31
    同时需要注意的是单个Redis实例的16个数据库的操作也都是共享这个单线程的,所以在设计时,如果16个数据库或者个都要存放数据并且读写较频繁,则推荐采用独立的Redis实例来保存各个数据库的数据,即使用不同的端口...
  • python之单线程多线程访问网站

    千次阅读 2018-04-16 16:20:14
    在了解多线程的时候我们需要先了解单线程问题。我对单线程做一简单的分析。 1、单线程问题 ——单线程就是我们最原始的方法,直接写出访问网站的脚本不需要任何多线程处理例如我们要访问300个网站,网站下载...
  • 而一个进程又是由线程所组成的。 什么是线程线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的, 即不同的线程可以执行同样的函数。 什么是高并发,及高...
  • Python多线程-手无的真相

    千次阅读 多人点赞 2021-02-24 15:44:46
    我们常说的「手无」其实类似多线程同时竞争一个共享资源的结果,要保证结果的唯一正确性,而这让我们从线程(Python)慢慢说起…… 文章目录 线程的概念 创建多线程 主线程 阻塞线程 线程方法 线程同步 同步的概念...
  • 昨天晚上做了个实验,用线程复制7个文件和单个线程复制7个文件(总共约1.7G)比较,代码如下:import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io....
  • 四、多线程单线程的区别 五、主线程与子线程之间的关系 在软件层面,多线程就是能同时从多个路线同时执行处理。从软件的角度来说,线程可以看作是同时的。即便在单核处理器的电脑上也能实现多线程。但是多个线程...
  • 单线程工作模式也成为同步模式。其就是在一定状态下只能做一件事情,比如我在18:00-18:30时间段可以做饭。 什么是多线程多线程工作模式也成为异步模式。其就是在一定状态下可以做多件事情,比如我在18:00-18:30...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 215,843
精华内容 86,337
关键字:

多线程比单线程慢