并发的第一步创建线程

     

 在并发编程中,线程的概念是非常重要的。说到线程,自然就要提到进程。下面就先厘清这两个概念。进程是运行在自己的地址空间内的自包容的程序,线程是在进程中的一个单一的顺序控制流。因此,单个进程是可以拥有多个线程。所以,这里我们可以知道,进程和线程的区别。 
       线程是很容易让人跟任务混在一起,事实上,两者根本不是同一个东西,只是经常捆在一起讲。准确的说,任务是由执行线程来驱动的,任务是附着在线程上的。
        如何创建线程呢?首先我们必须先定义我们的任务,因为线程是驱动任务的,没有搭乘的任务,就谈不上要去创建线程,就像列车开动,必须指明目的地,否则列车要怎么开啊。定义任务的方式很简单,就是一个实现Runnable的类,并且在这个实现中编写run()方法,来描述这个任务所要执行的命令。然后我们再将这个任务附着到线程上。这时,我们就需要Thread类。
Thread t = new Thread(new RunnableClass); 
t.start();
         Thread的构造器需要一个实现Runnable的对象,调用start()方法就是相当于初始化这个线程,它会自动调用run()方法,以便在这个线程中启动任务。注意,如果是在main()中执行这个线程,那么你将会发现,就算这个线程还没结束,main()中的其他语句还是会执行。为什么呢?因为main()也是你所写的进程的一个线程,只不过这个线程中包含其他线程而已。也就是说,任何一个线程都可以启动另一个线程。
          如果一个程序包含多个驱动任务的线程,那么,我们将会发现,这些线程的进行并不是有顺序的,而是不断交替进行的。至于如何交替,是由线程调度器自动控制的,而且每次执行结果是大不相同的,不确定的。
           但是除了上面那种方法,还有其他的方法,就是Executor(执行器)。Executor在客户端和任务执行之间提供了一个间接层,这个间接层将执行任务,而且允许管理异步任务的执行,而无须显示的管理线程的生命周期。如:ExecutorService exec = Executors.newCachedThreadPool();exec.executor(new RunnableClass);这里的CachedThreadPool是一种线程池,除此之外,还有FixedThreadPool。这两者有什么区别呢?前者在执行过程中会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,而后者,可一次性的预先的执行代价高昂的线程分配,可以限制线程的数量,如:ExecutorService exec = Executors.newFixedThreadPool(5).这样做是很有意义的,正如前面所说,一次性预先执行代价高昂的线程分配,这是什么意思呢?线程分配的代价是十分高昂的,相对于没有预先执行来说,因为要创建与所需数量相同的线程,那么它就得知道要多少数量啊,这个过程是我们并不太清楚的一个过程,但是我们知道,这是一个很费时费效率的过程,需要预先知道要分配多少个线程所节省的时间是很多的。但是,一般我们创建Executor的首选都是CachedThreadPool,为什么呢?就如前面所讲的,它能够在回收旧线程时停止创建新线程,这是很重要的,因为在任何线程池中,现有线程都会在可能的情况下被自动复用,而CachedThreadPool在线程结束时就会停止创建新线程,就不会发生滥用资源的现象,好吧,是一般下不会。所以,我们一般首选它作为创建Executor的方式,不要再执着于FixedThreadPool的那种省时的特性了,要知道,在设计时过早优化总是不好的,它很容易使我们忘记设计的核心而纠缠于那理不清的代码结构。
       既然创建线程的方式有两种,那么我们到底要选哪种呢?正如前面的代码所展示,显示的Thread对象是不便于对多个任务进行管理的,因为如果是不同的任务我们是需要再创建另一个显示的Thread对象,但是Executor相比起来更加便于管理这种多任务,因为只需再调用execute()方法就行,关于线程的创建,它会自动处理。
       说到Executor,必须提到SingleThreadExecutor,它就相当于线程数量为1的FixedThreadPool,如果向它提交多个任务,那么这些任务将排队,每个任务都会在下一个任务开始前运行结束,所有的任务都将使用同一个线程。这就像是一个正在排队的队伍,只有前面的任务结束才能轮到下一个任务。这样的方式就不需要在共享资源上同步了,因为任意时刻都只有一个任务在运行,并不会发生所谓的竞争。
原文地址:https://www.cnblogs.com/wenjiang/p/2660533.html