线程与并发_java

一:概述

   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线程池使用

  4、高级用法

原文地址:https://www.cnblogs.com/chunxiaozhang/p/12666817.html