• Java基础之多线程简述


      首先,要辨析进程与线程的概念:

      进程是程序执行的过程,它持有资源和线程,相对于程序本身而言具有动态性。

      线程是系统中最小的执行单元,同一个进程中可能有多个线程,它们共享该进程持有的资源。线程的通信也称为线程的交互,方式主要有互斥和同步。同步是指线程之间通过共同协作完成某项工作,线程间具有次序性;互斥是指线程间对某一资源的竞争,一次只能有一个线程访问该资源。

      介绍完了这些基本概念,下面简单介绍一下Java对多线程的支持.

      java中通过类Thread和接口Runnable来实现多线程的操作。它们都有一个run方法来指定线程工作时执行的代码。

    Thread类的常用方法

    类别

    名称

    简介

    线程的创建

    Thread()

     

    Thread(String name)

    Thread(Runnable target)

    Thread(Runnable target, String name)

    线程的方法

    void start()

    启动线程

    static void sleep(long millis)

    线程休眠

    static void sleep(long millis, int nanos)

    void join()

    某线程调用join方法后,使其他线程等待该线程终止

    void join(long millis)

    void join(long millis, int nanos)

    static void yield()

    当前正在运行的线程立刻释放处理器,重新加入竞争处理器的队列

    获取线程引用

    static Thread currentThread()

    返回当前正在运行的线程的引用

      使用多线程有两种方式,一种是直接继承Thread类,一种是实现Runnable接口

    • 继承Thread类,在run方法中指定需要线程执行的代码,通过getName()和setName()方法去访问线程名称。通过该类对象的start方法来开启线程。
    • 实现Runnable接口,重写run方法来指定需要线程执行的代码,然后把该类的对象作为参数传递给Thread对象来执行。

      另外,通过volatile关键字声明的成员变量可以保证当其他线程修改该成员变量后,本线程可以正确读取到此成员变量的值。

    线程的状态

      请结合OS中进程的5态来思考。以下线程状态和方法的介绍来自DreamSea(张小哲)

    • 新生状态(New): 当一个线程的实例被创建即使用new关键字和Thread类或其子类创建一个线程对象后,此时该线程处于新生(new)状态,处于新生状态的线程有自己的内存空间,但该线程并没有运行,此时线程还不是活着的(not alive);
    • 就绪状态(Runnable): 通过调用线程实例的start()方法来启动线程使线程进入就绪状态(runnable);处于就绪状态的线程已经具备了运行条件,但还没有被分配到CPU即不一定会被立即执行,此时处于线程就绪队列,等待系统为其分配CPCU,等待状态并不是执行状态; 此时线程是活着的(alive);
    • 运行状态(Running): 一旦获取CPU(被JVM选中),线程就进入运行(running)状态,线程的run()方法才开始被执行;在运行状态的线程执行自己的run()方法中的操作,直到调用其他的方法而终止、或者等待某种资源而阻塞、或者完成任务而死亡;如果在给定的时间片内没有执行结束,就会被系统给换下来回到线程的等待状态;此时线程是活着的(alive);
    • 阻塞状态(Blocked):通过调用join()、sleep()、wait()或者资源被暂用使线程处于阻塞(blocked)状态;处于Blocking状态的线程仍然是活着的(alive)
    • 死亡状态(Dead):当一个线程的run()方法运行完毕或被中断或被异常退出,该线程到达死亡(dead)状态。此时可能仍然存在一个该Thread的实例对象,当该Thread已经不可能在被作为一个可被独立执行的线程对待了,线程的独立的call stack已经被dissolved。一旦某一线程进入Dead状态,他就再也不能进入一个独立线程的生命周期了。对于一个处于Dead状态的线程调用start()方法,会出现一个运行期(runtime exception)的异常;处于Dead状态的线程不是活着的(not alive)。

     线程状态图

    Ø线程的方法(Method)、属性(Property)

    1)优先级(priority)

    每个类都有自己的优先级,一般property用1-10的整数表示,默认优先级是5,优先级最高是10;优先级高的线程并不一定比优先级低的线程执行的机会高,只是执行的机率高;默认一个线程的优先级和创建他的线程优先级相同;

    2)Thread.sleep()/sleep(long millis)

    当前线程睡眠/millis的时间(millis指定睡眠时间是其最小的不执行时间,因为sleep(millis)休眠到达后,无法保证会被JVM立即调度);sleep()是一个静态方法(static method) ,所以他不会停止其他的线程也处于休眠状态;线程sleep()时不会失去拥有的对象锁。 作用:保持对象锁,让出CPU,调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会;

    3)Thread.yield()

      让出CPU的使用权,给其他线程执行机会、让同等优先权的线程运行(但并不保证当前线程会被JVM再次调度、使该线程重新进入Running状态),如果没有同等优先权的线程,那么yield()方法将不会起作用。

    4)thread.join()

     使用该方法的线程会在此之间执行完毕后再往下继续执行。

    5)object.wait()  当一个线程执行到wait()方法时,他就进入到一个和该对象相关的等待池(Waiting Pool)中,同时失去了对象的机锁—暂时的,wait后还要返还对象锁。当前线程必须拥有当前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用。sleep()和wait()方法的最大区别是:sleep()睡眠时,保持对象锁,仍然占有该锁;而wait()睡眠时,释放对象锁。但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException(但不建议使用该方法)。

    6)object.notify()/notifyAll()

      唤醒在当前对象等待池中等待的第一个线程/所有线程。notify()/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常。

    7)Synchronizing Block

     Synchronized Block/方法控制对类成员变量的访问;Java中的每一个对象都有唯一的一个内置的锁,每个Synchronized Block/方法只有持有调用该方法被锁定对象的锁才可以访问,否则所属线程阻塞;机锁具有独占性、一旦被一个Thread持有,其他的Thread就不能再拥有(不能访问其他同步方法),方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

    正确停止Java线程

      非正确停止的方法:stop()

      要正确停止线程,应该使用正确的退出标记。正常情况下run方法执行完毕线程就停止了,但是有些情况下run方法中需要while循环保持轮询,所以应该为while循环设置合适的退出条件,即退出标记,保证线程正确停止。

      或者使用interrupt()方法的初衷并不是要停止线程,正常情况下,调用该方法可以将中断标记设置为TRUE。但是,当某个线程因为调用了wait()或者sleep()等方法而被阻塞时,此时调用interrupt()方法并不能正确的将interrupted标记设置为TRUE,并且同时会抛出中断异常。

    争用条件:当多个线程同时共享访问同一个内存数据时,每个线程都尝试操作该数据,从而导致数据被破坏。

    线程的互斥与同步

    互斥是指在同一时刻只有一个线程可以对临界区进行操作。在JAVA中,可以通过synchronized块或者方法来实现。

    在synchronized块中,需要对某个对象进行加锁,并将需要互斥的代码放入synchronized块中,从而来实现互斥行为。获得该锁的进程可以进入该synchronized块中.

    而同步则是线程之间的一种通信机制,通过同步可以规定线程执行的顺序。例如,当线程不满足访问某资源时,可以通过调用锁对象的lock.wait()方法,使该线程让出CPU,让出锁,并在该锁对象的waitset中等待。满足条件时,可以通过lock.notify()方法随机唤醒一条线程,lock.notifyAll()则唤醒该waitset中所有的线程,使它们重新竞争该锁。被唤醒的线程从wait()方法处继续执行。

  • 相关阅读:
    start with connect by prior 递归查询用法(二)
    start with connect by prior 递归查询用法(一)
    oracle之spool详细使用总结
    关于ETL面试相关
    ETL常用的三种工具介绍及对比Datastage,Informatica和Kettle
    Oracle执行计划详解
    随机函数的使用(DBMS_RANDOM)
    oracle中的替换函数replace和translate函数
    ces
    文章11
  • 原文地址:https://www.cnblogs.com/cqumonk/p/4174084.html
Copyright © 2020-2023  润新知