什么是多线程?多线程就是同时执行多个线程。举个简单易懂的例子,多线程相当于一条路上的多条车道。车辆在单车道行驶速度慢,可能造成拥堵,多车道行驶可以缓解速度和拥堵。多线程不是为了提高程序的执行速度,而是为了提高程序的利用率。多线程在开发中的应用场景1、后台任务,比如定时给大量用户(100w以上)发送邮件;2.异步处理,例如:发送微博,记录日志等;3、分布式计算其实web服务器本身就是采用了多线程;以及各种专用服务器(如游戏服务器);创建线程的三种方式第一种方式是创建并继承Thread类,重写Thread中的run方法。第二种方式是创建一个继承的Thread类,重写Thread中的run方法。第三种方式是创建并实现Callable接口,重写call()方法。有返回值的任务必须实现Callable接口,同样,没有返回值的任务必须有Runnable接口。Callable任务执行后,可以得到一个Future对象,在该对象上调用get可以得到Callable任务返回的Object。结合线程池接口ExecutorService,可以实现传说中的多线程返回结果。多线程特征代码的运行结果与代码执行顺序之后调用代码的先后顺序无关,即线程是一个子任务,CPU随机调用线程中的方法。注意:1、Thread中的start()方法不能被多次调用,否则会抛出IllegalThreadStateException。2、启动线程的方法不是run()方法而是start方法。如果调用run()方法,是同步的,不能异步执行。3、start()方法的执行顺序不代表线程启动的顺序。也就是说,越早调用线程的start()方法,就越早执行run()方法。什么是可调用方法?Callable接口属于Executor。与Runnable接口相比,区别在于:(1)。Callable可以在任务结束后提供一个返回值,而Runnable没有这个功能。(2).抛出异常,但是Runnable的run()方法不能抛出异常(3)。运行Callable可以得到一个Future对象,它专门代表异步计算的结果,它提供了一个检查计算是否完成的方法。由于线程属于异步计算模型,无法从其他线程获取函数的返回值。这种情况下,可以使用Future监听目标线程的call()方法,调用Future的get()方法,当得到结果时,当前线程会被阻塞,直到call()方法返回完结果。其实还有一种情况,使用的线程池更适合那样实现多线程。为什么?实现多线程的推荐方法是实现Runnable接口。原因:(1)。Thread类定义了多种可以被派生类使用或重写的方法,但只有run()方法是必须重写的。在run()方法中实现这个线程的main函数,这是Runnable接口需要实现的方法(2)。通过继承Thread实现方法来实现Runnable接口的效果是一样的,Java只能是单继承多实现。如果一个类中已经继承了其他需要的类,那么就需要实现一个接口。线程的声明周期当一个线程被创建并启动时,它既不会一启动就进入执行状态,也不会一直处于执行状态。在一个线程的生命周期中,会经历五个状态:New、Runnable、Running、Blocked、Dead。尤其是当一个线程启动的时候,它不可能一直“占用”CPU单独运行,所以CPU需要在多个线程之间切换,所以线程状态也会多次在运行和阻塞之间切换。终止线程的4种方法1.程序正常结束。2.使用退出标志退出线程。定义了退出标志exit。当exit为真时,while循环退出。exit的默认值为false。定义exit时,使用了Java关键字volatile。关键字的目的是让exit同步,也就是说同一时刻只能有一个线程修改exit的值。publicclassThreadSafeextendsThread{publicvolatilebooleanisExit=false;publicvoidrun(){while(!isExit){//todomysomething}}}3.使用interrupt()方法中断线程有两种情况:12.线程处于阻塞状态:如果使用了sleep、waitforsynchronizationlock、receiver、acceptinsocket等方法,线程就会处于阻塞状态。当调用线程的interrupt()方法时,将抛出InterruptException。阻塞中的方法抛出这个异常,通过代码捕获这个异常,然后跳出循环状态,让我们有机会结束这个线程的执行。通常很多人认为只要调用中断方法,线程就会结束,其实是错误的,一定要先捕获InterruptedException,然后通过break跳出循环,正常结束运行。方法二、线程不阻塞:使用isInterrupted()判断线程的中断标志退出循环。使用interrupt()方法时,中断标志将被设置为true,这与使用自定义标志来控制循环是一样的。4.stop方法终止线程(线程不安全)。可以直接在程序中使用thread.stop()强制终止线程,但是stop方法是非常危险的,就像突然关闭电脑电源而不是按正常程序关机一样。会产生不可预知的结果,不安全的主要原因是:调用thread.stop()后,创建子线程的线程会抛出ThreadDeatherror错误,并会释放子线程持有的所有锁。通常,任何被锁定的代码块都是为了保护数据的一致性。如果线程持有的所有锁在调用thread.stop()后突然被释放(不可控),那么受保护的数据可能会出现不一致,其他使用损坏数据的线程可能会导致一些奇怪的应用程序错误。因此,不推荐使用stop方法终止线程。线程池介绍线程池:线程池,java.util.concurrent.Executors提供了java.util.concurrent.Executor接口的实现,用于创建线程池。它是一种线程使用模式。线程池维护着多个线程,等待主管分配可以并发执行的任务。多线程技术主要解决处理器单元中多线程执行的问题,可以显着减少处理器单元的空闲时间,提高处理器单元的吞吐量。线程池是多线程的一种形式,其中任务被添加到队列中,然后在创建线程后自动启动。线程池线程都是后台线程。每个线程使用默认堆栈大小,以默认优先级运行,并且位于多线程单元中。如果一个线程在托管代码中空闲(比如等待一个事件),线程池将插入另一个工作线程来保持所有处理器忙碌。如果所有的线程池线程一直处于忙碌状态,但队列中有待处理的工作,线程池会在一段时间后创建另一个工作线程,但线程数永远不会超过最大值。超过最大值的线程可以排队,但在其他线程完成之前它们不会启动。Java中的4个线程池newCachedThreadPool创建一个线程池,它根据需要创建新的线程,但是当它们可用时会重用以前构造的线程。对于执行许多短期异步任务的程序,这些线程池通常会提高程序性能。调用execute将重用先前构造的线程(如果线程可用)。如果没有可用的现有线程,则会创建一个新线程并将其添加到池中。终止并从缓存中删除60秒内未使用的线程。因此,长时间处于空闲状态的线程池不会使用任何资源。newFixedThreadPool创建一个具有固定数量线程的可重用线程池,并在共享无界队列中运行这些线程。在任何时候,最多nThreads个线程将是活动的处理任务。如果在所有线程都处于活动状态时提交了附加任务,则附加任务将在队列中等待,直到有线程可用。如果任何线程在关闭之前的执行过程中由于失败而终止,则新线程将用后续任务替换它(如果需要)。池中的线程将一直存在,直到一个线程被显式关闭。newScheduledThreadPool创建一个线程池,可以安排在给定延迟后或定期运行命令。newSingleThreadExecutorExecutors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或者发生异常时)重新启动一个线程来代替原来的线程继续执行!线程重用每个Thread类都有一个start方法。当调用start启动一个线程时,Java虚拟机调用这个类的run方法。然后这个类的run()方法调用Runnable对象的run()方法。我们可以继承并重写Thread类,将连续循环调用中传入的Runnable对象添加到它的start方法中。这就是线程池的实现原理。loop方法中连续获取Runnable是用Queue实现的,可以阻塞直到获取到下一个Runnable。线程池的组成一般的线程池主要分为以下四个部分:线程池管理器:用于创建和管理线程池工作线程:线程池中的线程任务接口:每个任务必须实现的接口,使用为工作线程调度其运行任务队列:用于存储待处理任务,提供缓冲机制。Java中的线程池是通过Executor框架实现的,Executor框架使用了Executor、Executors、ExecutorService、ThreadPoolExecutor、Callable和Future、FutureTask这几个类。ThreadPoolExecutor的构造方法如下:corePoolSize:指定线程池中的线程数。maximumPoolSize:指定线程池中的最大线程数。keepAliveTime:当当前线程池数量超过corePoolSize时,冗余空闲线程的存活时间,即多次销毁。unit:keepAliveTime的单位。workQueue:任务队列,已经提交但还未执行??的任务。threadFactory:线程工厂,用于创建线程,一般使用默认。handler:拒绝策略,当需要处理的任务过多时,如何拒绝任务。Java线程池工作进程线程池刚创建的时候,里面没有一个线程。任务队列作为参数传入。但是,即使队列中有任务,线程池也不会立即执行。当调用execute()方法添加任务时,线程池会做如下判断:a)如果正在运行的线程数小于corePoolSize,则立即创建一个线程来运行这个任务;b)如果正在运行的线程数大于等于corePoolSize,则将此任务放入队列;c)如果此时队列已满,运行的线程数小于maximumPoolSize,那么仍然需要立即创建一个非核心线程来运行这个任务;d)如果队列已满,并且正在运行的线程数大于等于maximumPoolSize,线程池将抛出异常RejectExecutionException。当一个线程完成一个任务时,它会从队列中取出下一个任务来执行。当线程无事,超过一定时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,则停止该线程。所以线程池的所有任务完成后,最终会收缩到corePoolSize的大小。线程池中有哪几种工作队列?ArrayBlockingQueue是一个基于数组结构的有界阻塞队列。该队列根据FIFO(先进先出)原则对元素进行排序。LinkedBlockingQueue是一个基于链表结构的阻塞队列。该队列按照FIFO(先进先出)对元素进行排序,其吞吐量通常高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用这个队列。SynchronousQueue不存储元素的阻塞队列。每次插入操作必须等到另一个线程调用移除操作,否则插入操作一直阻塞,吞吐量通常高于静态工厂方法Executors.newCachedThreadPool使用的LinkedBlockingQueue。PriorityBlockingQueue具有优先级的无限阻塞队列。线程池原理线程池的工作主要是控制运行线程的数量。在处理过程中,将任务放入队列中,然后创建线程后启动这些任务。如果线程数超过最大数,则超出的线程数排队等待其他线程执行完后,再从队列中取出任务执行。它的主要特点是:线程多路复用;控制最大并发数;管理线程。原作者:cideGoogler
