Java多线程

线程概述

  当一个程序进入内存中,即变成一个进程,进程是处于运行过程中的程序

  并行和并发:

    并行:是同一时刻,有多条指令在处理器上运行

    并发:同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个线程同时执行的效果

  多线程的优势:

    。。。。。。。。

线程的创建和启动:

  1.继承Thread类来创建并启动线程线程:

     继承Thread类,重写run()方法,run()为线程执行体,创建Thread的子类,调用start()方法启动线程

     main()方法代表主线程的执行体

      

    Thread.currentThread():返回当前正在执行的线程的对象(静态方法)

    getName():返回调用该方法的线程名字(实例方法)

    多个线程之间无法共享线程类的实例变量

  

 

  2.实现Runnable接口创建线程类:

    定义Runnable接口的实现类,并重写run()方法(线程执行体)

    创Runnbale实现类的实例,用此实例作为Thread的target来创建Thread对象

    调用start()方法启动线程

    这种创建线程,多个线程可以共享一个target,即多个线程可以共享同一个线程类的实例变量

 

  3.使用Callable和Future创建线程:

    Callable的call()方法可以作为线程体来执行,可以有返回值,可以声明抛出异常,**Callable对象不能直接作为Thread的Target()

    Future接口来代表Callable接口的返回值,提供了Future接口的实现类,并实现了Runnable接口--可以作为Thread类的Target

    创建Callable接口的实现类,并实现call()方法,创建Callable实现类的实例,可以通过Lambda的表达式创建callable的对象

    使用FutureTask类来包装Callable对象

    使用FutureTask类的实例作为Thread的target创建线程,调用start()方法启动线程

    FutureTask对象的get()方法来获得返回值

 

   推荐接口创建线程

 

线程生命周期

  新建和就绪状态:

    调用start()方法后线程处于就绪状态,但并没有立即运行,线程何时开始运行由JVM里线程调度器来调度

    不能直接调用run()方法,直接调用run()方法的话还是单线程来的

    不要重复调用start()方法,会抛出IllegalThreadStateException

    如果想调用start()方法后线程立即运行,可以使用Thread.sleep(1)来让当前线程睡眠1毫秒,在这1毫秒内cpu会执行另一个处于就绪状态的线程

  运行和阻塞状态:

    p725页线程状态转换图 

  线程死亡:

    线程体执行结束,调用stop()方法,抛出异常,后处于死亡状态

    线程死亡后不要再调用start()方法

 

 

线程控制:

  join线程:

    当被join的线程调用join后,当前运行的线程会被阻塞,直到被join()的线程执行完/等待到指定时间

 

  后台线程:调用Thread()对象的setDaemon(true)后将此线程设置成后台线程--又被称为守护线程/精灵线程(设置为后台线程的方法必须在start()前调用)

       如果所有前台线程死亡,后台线程也会自动死亡

     Thread类的isDaemon()方法用来判断线程是否为后台线程

     主线程默认是前台线程,前台线程创建的子线程默认是前台线程,后台线程创建的线程默认为后台线程

 

  线程睡眠:sleep 静态方法

     线程调用此方法后进入阻塞状态

  

  线程让步:yield 静态方法

     与sleep类似,但只是让当前线程进入就绪状态,等待再次调度,不会阻塞线程(只会给优先级比当前线程高的线程执行机会)

 

  改变线程优先级:

      setPriority( int k) 1-10 尽量使用提供的静态常量设置优先级

 

 

线程同步

  线程安全问题:取钱问题

 

  同步代码块:

    两个进程并发修改同一个文件时就有可能出现异常,java引入同步监视器来解决这个问题,通用方法---同步代码块

    synchronized(obj){同步代码块}  obj为同步监视器,****线程开始执行同步代码块前必须要先获得对同步监视器的锁定(任何一个时刻智能有一个线程获得对同步代码的锁定)

    通常使用并发访问的共享资源充当同步监视器

 

  同步方法:使用synchronized修饰某个方法。则该方法为同步方法

    同步方法可以方便实现线程安全类()

 

  释放同步监视器的锁定(程序无法显示释放对同步监视器的锁定)

 

  同步锁(Lock)

    Java5开始提供更强大线程同步机制--通过显式定义同步锁对象来实现同步,同步锁由Lock对象充当

    Lock ReadWriteLock是Java5提供的两个根接口,ReentrantLock(可重入锁)实现了Lock接口, ReentrantReadWriteLock实现了ReadWriteLock接口

    StampedLock类

      加锁  --- 修改 ---释放

 

  死锁:两个线程互相等待对方释放同步监视器

 

 

线程通信

  传统线程通信:使用wait() notify() notifyAll()实现线程同步(为Objectt提供的方法),必须由同步监视器对象来调用这三个方法

  wait()当前线程等待

  notify()唤醒在当前监视器等待的线程,随机的

  notifyAll()唤醒在此同步监视器上等待的所有进程

 

  使用Condition控制线程通信(在用Lock实现同步的情况下)

    Condition对象被绑定在Lock对象的实例上,通过调用newCondition()获得Condition对象

    Condition对象的await()方法

            signal()

            signalAll() 和传统线程的三个方法类似

  

  使用阻塞队列(BlockingQueue)控制线程通信

    虽然是Queue的子接口,但主要用途是作为线程同步工具

        基于队列的线程控制,尾部放入,头部取出

 

 

 

线程组和未处理的异常

  ThreadGroup实现对一批线程进行分类管理,Thread在创建的时候可以指定所属的线程组(一旦某个线程加入某个线程组后中途不能更改)

 

线程池:

  Executors()工厂来生产线程池 返回ExecutorService对象代表尽快执行的线程池

 

  线程的创建成本较高,用线程池,线程执行完后不会死亡,而是回到线程池称为空闲状态,等待接收再次执行

 

  Java8增强的ForkJoinPool:是ExecutorService的实现类,是一种特殊的线程池

    

    

      

 

     

    

    

    

posted @ 2018-03-15 20:41  啸辰  阅读(123)  评论(0编辑  收藏  举报