一:概述
1、概念解释
进程与线程
进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
线程:是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的 资源。
并发与并行
并发:是指同一个时间段内多个任务同时都在执行,并且都没有执行结束。强调在一个时间段内同时执行,而一个时间段由多个单位时间累积而成,并发的多个任务在单位时间内不一定同时在执行 。
并行:是说在单位时间内多个任务同时在执行 。
2、线程特性
线程特性: 1、线程能被标记为守护线程,也可以是用户线程 2、每个线程均分配一个name,默认为(Thread-自增数字)的组合 3、每个线程都有优先级.高优先级线程优先于低优先级线程执行. 1-10,默认为5 4、main所在的线程组为main,构造线程的时候没有现实的指定线程组,线程组默认和父线程一样 5、当线程中的run()方法代码里面又创建了一个新的线程对象时,新创建的线程优先级和父线程优先级一样. 6、当且仅当父线程为守护线程时,新创建的线程才会是守护线程. 7、当JVM启动时,通常会有唯一的一个非守护线程(这一线程用于调用指定类的main()方法) JVM会持续执行线程直到下面情况某一个发生为止: 1)类运行时exit()方法被调用 且 安全机制允许此exit()方法的调用. 2)所有非守护类型的线程均已经终止、run()方法调用返回、run()方法外部抛出了一些可传播性的异常. java分为两种线程:用户线程和守护线程 守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时, 程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。 守护线程和用户线程唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 没有了被守护者,守护线程也就没有继续运行的必要了。 线程状态: NEW:状态是指线程刚创建, 尚未启动 RUNNABLE:状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等。 BLOCKED:这个状态下, 是在多个线程有同步操作的场景, 比如等待另一个线程synchronized执行释放, 或可重入synchronized别人调用wait()方法, 也就是这里是线程在等待进入临界区。 WAITING:这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll以便该线程可以继续下一步操作。 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在临界点里面wait等待别人notify, 线程调用了join方法也会进入WAITING状态, 等待被他join的线程执行结束。 TIMED_WAITING:这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态。 TERMINATED: 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)。
二:使用说明
1、线程创建
继承THread类 public class ExtendsThread extends Thread{ @Override public void run() { System.out.println ( "继承Thread类创建线程。。。。。。。" ); } } 1)、在run()方法内获取当前线程直接使用this就可以了,无须使用Thread.currentThread()方法。 2)、是Java不支持多继承,如果继承了Thread类,那么就不能再继承其他类。 3)、另外任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码。 实现Runnable接口 public class RunnableThread implements Runnable{ @Override public void run() { System.out.println ( "实现Runnable接口的线程。。。。。" ); } } 匿名内部类实现的线程 public class MainThread { public static void main(String[] args) { new Thread ( new Runnable ( ) { @Override public void run() { System.out.println ( "匿名内部类实现的线程。。。。。" ); } } ).start ();//启动线程 } } 实现callable接口 public class callableThread implements Callable { @Override public Object call() throws Exception { System.out.println ( "实现callable接口开启线程。。。。。。" ); return true; } } tips: 1、千万不要调用run方法,如果调用run方法好比是对象调用方法,依然还是只有一个线程,并没有开启新的线程。start()方法开启线程,只能启动一次。 2、callable接口和runnable接口基本相同,callable接口有返回值,更方便使用,runnable接口则无法使用发挥至。 3、Callable能够抛出checked exception,而Runnable不可以。 4、Callable和Runnable都可以应用于executors(java内置线程池管理工具)。而Thread类只支持Runnable。 5、Callable与executors联合在一起,在任务完成时可立刻获得一个更新了的Future(用于获取线程返回结果的接口)。而Runable却要自己处理。
2、方法解释
start() start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。 run() run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。 sleep() sleep(long millis) //参数为毫秒 sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒 sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务,不会释放锁。 yield() yield()方法和sleep()方法有点相似,,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入到就绪状态。调用yield方法会让当前线程交出CPU权限, 让CPU去执行其他的线程。它跟sleep方法类似,同样。但是yield不能控制具体的交出CPU的时间,另外,当某个线程调用了yield()方法之后,只有优先级与当前线程相 同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。调用yield方法是让线程重回就绪状态,需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。 join() join() join(long millis) //参数为毫秒 join(long millis,int nanoseconds)//第一参数为毫秒,第二个参数为纳秒 假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了 时间参数的join方法,则等待一定的事件。当调用thread1.join()方法后,main线程会进入等待,等待thread1执行完之后再继续执行。 实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知,wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。 interrupt() interrupt即中断,单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程。 interrupted() interrupted()函数是Thread静态方法,用来检测当前线程的interrupt状态,检测完成后,状态清空。此方法首先调用isInterrupted方法,测试线程是否已经中断,参数决定是否重置中断标志。 stop() stop是一个废弃的不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。 destroy() destroy方法也是废弃的方法。基本不会被使用到。 线程属性方法: getId:用来得到线程ID getName和setName:用来得到或者设置线程名称。 getPriority和setPriority:用来获取和设置线程优先级。 setDaemon和isDaemon:用来设置线程是否成为守护线程和判断线程是否是守护线程。 currentThread():用来获取当前线程。
3、线程池使用
java线程池使用
Executors Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,内容全部在java.util.concurrent下面,Java5的线程池分好多种:具体的可以分为两类,固定尺寸的线程池、可 变尺寸连接池。 在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。 Executors提供四种线程池(ExecutorService),分别为: Executors.newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 Executors.newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 Executors.newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 Executors.newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 ExecutorService(线程池管理类) execute(Runnable)接收壹個 java.lang.Runnable 对象作为参数,并且以异步的方式执行它。 submit(Runnable)接收壹個 Runnable 的实现作为参数,但是会返回壹個 Future 对象。这個 Future 对象可以用于判断 Runnable 是否结束执行。 submit(Callable)与方法 submit(Runnable) 比较类似。Callable 的实例与 Runnable 的实例很类似,但是 Callable 的 call() 方法可以返回壹個结果。方法 Runnable.run() 则不能返回结果。 inVokeAny()接收壹個包含 Callable 对象的集合作为参数。调用该方法不会返回 Future 对象,而是返回集合中某壹個 Callable 对象的结果,无法保证哪壹個 Callable。 invokeAll()会调用存在于参数集合中的所有 Callable 对象,并且返回壹個包含 Future 对象的集合,你可以通过这個返回的集合来管理每個 Callable 的执行结果。 shutdown()方法会等待线程都执行完毕之后再关闭,但是如果调用的是shutdownNow()方法,则相当于调用每个线程的 interrupt()方法。 Future 在并发编程中,我们经常用到非阻塞的模型,继承thread类还是实现runnable接口,都无法保证获取到之前的执行结果。通过实现Callback接口,并用Future可以来接收多线程的执行结果。 Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。 Future方法: cancel()方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。 参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。 如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false; 如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false; 如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。 isCancelled()方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。 isDone()方法表示任务是否已经完成,若任务完成,则返回true; get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回; get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。 FutureTask()-------FutureTask 实现 RunnableFuture接口,RunnableFuture 继承 Runnable接口,Future接口。 FutureTask执行多任务计算:利用FutureTask和ExecutorService,可以用多线程的方式提交计算任务,主线程继续执行其他任务,当主线程需要子线程的计算结果时,在异步获取子线程的执行结果。 FutureTask保任务只执行一次:在很多高并发的环境下,往往我们只需要某些任务只执行一次。这种使用情景FutureTask的特性恰能胜任。 FutureTask与Callable使用实例 callable类 public class callableThread implements Callable { private int i; public callableThread(int i) { this.i = i; } @Override public Object call() throws Exception { //越早开始执行,i值越小,睡眠时间越长,以便于测试返回结果是否与futuretask执行顺序一致 Thread.sleep ( (8-i) * 1000 ); System.out.println ( "实现callable接口开启线程。。。。。。,"+"睡眠(毫秒):"+ (8-i) * 1000); return i; } } 测试类: public class FutureAndCallable { public static void main(String[] args) throws ExecutionException, InterruptedException { //创建定长线程池管理类 ExecutorService executorService = Executors.newFixedThreadPool ( 5 ); //FutureTask添加线程 List<FutureTask<Object>> taskList = new ArrayList<> ( ); for (int i = 0 ; i < 5 ; i++) { callableThread callableThread = new callableThread (i); taskList.add ( new FutureTask<> (callableThread) ); } //由管理类异步启动执行线程 for (FutureTask futureTask:taskList) { executorService.submit ( futureTask ); } //关闭线程,会阻塞直到最后一个相乘执行完毕 executorService.shutdown (); //通过futuretask拿取线程执行结果 for (FutureTask futureTask:taskList) { Object obj = futureTask.get ( ); System.out.println ( obj); } } } 测试结果: 实现callable接口开启线程。。。。。。,睡眠(毫秒):4000 实现callable接口开启线程。。。。。。,睡眠(毫秒):5000 实现callable接口开启线程。。。。。。,睡眠(毫秒):6000 实现callable接口开启线程。。。。。。,睡眠(毫秒):7000 实现callable接口开启线程。。。。。。,睡眠(毫秒):8000 0 1 2 3 4
spring线程池使用