Java基础之多线程简述

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

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

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

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

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

Thread类的经常使用方法blog

类别继承

名称接口

简介生命周期

线程的建立队列

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()方法处继续执行。

相关文章
相关标签/搜索