线程

1.线程状态:(6种,可以通过getState方法获得)

  New(新创建):new一个新线程时,如new Thread(r),该线程还没有开始。它的状态是new,在线程运行之前还有一些工作要做。

  Runnable(可运行):一旦调用start方法,线程处于runnable状态。可能正在运行,也可能没有运行。在线程之间切换时,需要根据线程的优先级决定(抢占式调度)。

  Blocked(被阻塞):不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。当一个线程试图获取一个内部的对象锁,而改锁被其他线程持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程变成非阻塞状态。

  Waiting(等待):当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。在调用Object.wait方法或Thread.join方法,或者是等待Lock或Condition时,就会出现这种情况。

  Timed waiting(计时等待):有几个方法有一个超时参数。调用它们导致线程进入计时等待状态。这一状态将一直保持到超时期满或者接受到适当的通知。(Thread.sleep,Object.wait,Thread.join,Lock.tryLock,Condition.await的计时版)

  Terminated(被终止):线程被终止原因:a.因为run方法正常退出而自然死亡。b.因为一个没有捕获的异常终止了run方法而意外死亡。

  Thread

  void join():等待终止指定的线程。

  void join(long millis):等待指定的线程死亡或者经过指定的毫秒数。

  Thread.State getState():返回状态。

2.线程属性

  a.线程优先级:可以将优先级设置为MIN_PRIORITY(在Thread类中定义为1)与MAX_PRIORITY(定义为10)之间的任何值。线程优先级高度依赖于系统。

  Thread

  void setPriority(int newPriority):优先级

  static int MIN_PRIORITY:最小优先级值为1

  static int NORM_PRIORITY:默认优先级值为5

  static int MAX_PRIORITY:最高优先级值为10

  static void yield():导致当前执行线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来会被调度。这是一个静态方法。

3.守护线程

  可以通过调用t.setDaemon(true);将线程转换为守护线程(daemon thread)。守护线程唯一用途是为其他线程提供服务。守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的事件发生中断。

4.未捕获异常处理器

  线程的run方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程中止。在这种情况下,线程就死了。但是不需要任何catch子句来处理可以被传播的异常。相反,在线程死亡之前,异常就被传递到一个用于捕获异常的处理器。

  该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类。这个接口只有一个方法。

  void uncaughtException(Thread t ,Throwable e)

  可以用setUncaughtExceptionHandler方法为任何线程安装一个处理器。也可以用Thread类的静态方法setDefaultUncaughtExceptionHandler为所有线程安装一个默认的处理器。替换处理器可以使用日志API发送未捕获异常的报告到日志文件。

  如果不安装默认的处理器,默认的处理器为空。但是,如果不为独立的县城安装处理器,此时的处理器就是该线程的ThreadGroup对象。

5.同步

  竞争条件:多个线程同时需要对一个数据进行修改。

6.锁对象

  有两种机制防止代码块受访问并发的干扰。synchronized关键字和ReetrantLock类。synchronized关键字自动提供一个锁以及相关的“条件”,对于大多数需要显式锁的情况,这是很便利的。

  用ReentrantLock保护代码块的基本结构如下:

  myLock.lock();//a ReentrantLock object

  try{}

  finally{myLock.unclock();//确保锁是解锁的及时异常被抛出。}

  这一结构确保任何时刻只有一个线程进入临界区。

  锁是可重入的。因为线程可以重复地获得已经持有的锁。锁保持一个持有计数来跟踪对lock方法的嵌套调用。线程在每一次调用lock都要调用unlock来释放锁。由于这一特性,被一个锁保护的代码可以调用另一个使用相同的锁的方法。

  java.util.concurrent.locks.Lock

  void lock():获取这个锁,如果所同时被另一个线程拥有则发生阻塞。

  void unlock():释放这个所

  java.util.concurrent.locks.ReentrantLock

  ReentrantLock():构建一个可以被用来保护临界区的可重入锁。

  ReentrantLock(boolean fair):构建一个带有公平策略的锁。一个公平锁偏爱等待时间最长的线程。但是会降低性能。默认情况下,锁没有被强制为公平。(如果线程调度器选择忽略一个线程,而该线程为了这个锁已经等待了很长时间,那么就没有机会公平处理这个锁了。)

 7.条件对象

  使用一个条件对象来管理那些已经获得了一个锁但是却不能做有用工作的线程。条件对象也被称为条件变量。

  一个锁对象可以有一个或多个相关的条件对象。可以用newCondition方法获得一个条件对象。习惯给每一个条件对象命名为可以反映它所表达的条件的名字。

  例如:余额充足:

  class Bank{

    private Condition sufficientFunds;

    ...

    public Bank(){

    ...

    sufficientFunds = bankLock.newCondition();

    }

   }

  sufficientFunds.await();使当前线程被阻塞,并放弃了锁。

  等待获得所得线程和调用await方法的线程存在本质上的不同。一旦一个县城调用await方法,它进入该条件的等待集。当锁可用时,该线程不能马上解除阻塞。相反,它处于阻塞状态,直到另一个线程调用同一条件上的signalAll方法时为止。sufficientFunds.signalAll();重新激活因为这一条件而等待的所有线程。当这些线程从等待集当中移出时,他们再次成为可运行的,调度器将再次激活它们。同时,他们将试图重新进入该对象。一旦锁成为可用的,他们中的某个将从await调用返回,获得该锁并从被阻塞的地方继续执行。此时,线程应该再次测试该条件。

  通常,对await的调用应该在如下形式的循环体中

  while(!(ok to proceed)) condition.await();当不满足进程条件时,条件对象进入等待。

  当一个线程调用await时,它没办法重新激活自身。如果没有其他线程调用signalAll方法来重新激活等待的线程,就会导致死锁(deadlock)现象。如果所有线程被阻塞,该程序就挂起了。

  signal方法随机解除等待集中某个线程的阻塞状态。存在风险,多国随机选择的发现自己仍然不能运行,那么再次阻塞,没有其他线程再次调用signal方法,系统就死锁了。

原文地址:https://www.cnblogs.com/wongem/p/6728971.html