线程 - Java2详解

来源:互联网 发布:java异常关键字怎么用 编辑:程序博客网 时间:2024/05/19 18:12

1. 多线程 -core java 7 第2卷

1.3 线程状态 -core java7

线程可以有4个状态:
    New(新生)  即new Thread(r); 此时线程还没有开始运行(start).
    Runnable(可运行)  即调用了start方法. 之后当系统给它分配cpu时间时它就运行.
                     所以这个状态下. 线程可能正在运行也可能正在等待时间片.
    Blocked(被阻塞)  即可运行的线程调用了sleep或wait方法.或I/O时被阻塞.或等待一个锁.
    Dead(死亡) 即线程的run方法执行完毕.或者是run方法抛出了未捕获的异常.
              要判断一个线程是否还或者(即处于"可运行"或"被阻塞"状态). 可调用isAlive()方法.

4种状态的变化图如下:

新生
  |
可运行 <---> 被阻塞
  |
死亡

 

1.4 线程属性

包括: 线程优先级. 守护线程. 线程组. 未捕获异常的处理器.

1.4.1 线程优先级
通过setPriority()和getPriority()两个方法来查看/设置.

1.4.2 守护线程
通过isDaemon()和setDaemon()来查看/设置一个线程是否为"守护线程".
守护线程的作用是为其它线程提供服务. 例如计时器线程可以定时给其它线程发送消息.
当所有的线程都是守护线程时. 虚拟机就退出. 因为其它线程都结束了都不需要服务了.

1.4.3 线程组
例如一个Internet浏览器中.有很多线程都在从服务器上获取图片. 此时用户点击了Stop按钮来中断当前页面
的载入. 这时就应该用线程组. 将所有的线程放在线程组中一起来方便的操作.
创建线程组用:
    ThreadGroup(String name)
    ThreadGroup(ThreadGroup parent, String name) //第一个参数指定了新线程组的父线程组.
                                     //缺省时, 父线程组是当前线程的线程组.

要将线程放入指定的线程组. 可以在创建线程对象时在构造方法中指定:
    Thread(ThreadGroup g, String name) //线程的构造方法
之后可以通过Thread类的方法
    ThreadGroup getThreadGroup()
来得到一个线程的线程组.

线程组(ThreadGroup类)的方法有:
    int activeCount() //取得线程组中活线程的数量.
    int enumerate(Thread[] list)  //得到线程组中的活线程, 将它们放入参数的数组中.
    ThreadGroup getParent()  //返回线程组的父线程组.
    void interrupt()  //中断该线程组和它的子线程组中的所有线程.

1.4.4 未捕获异常处理器
runnable接口的run()方法没有声明抛出异常. 所以它不会抛出"已检查异常".
但"未检查异常"却可以抛出.而这会导致线程死亡.

可以给线程安装一个"未捕获异常处理器" 来处理从run()抛出的异常.
处理器是实现了Thread.UncaughtExceptionHandler接口的类. 该接口有唯一的方法:
    void uncaughtException(Thread t, Throwable e) //参数接收抛出异常的线程和抛出的异常.

要给线程安装"未捕获异常处理器" . 使用Thread类的方法:
    void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
对应的获取该处理器用的方法为:
    Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
注意. 如果你没有设置一个线程的处理器. 则该处理器就是线程的ThreadGroup对象. 因为ThreadGroup类
也实现了Thread.UncaughtExceptionHandler接口. 它的uncaughtException()方法做如下操作:
    若该线程组有父线程组. 则调用父线程组的uncaughtException()方法
    否则.若使用后边的静态方法setDefaultUncaughtExceptionHandler给所有线程安装了缺省处理器.
         则用该缺省处理器处理.
    否则.若抛出的异常(Throwable)是一个ThreadDeath实例.则什么都不做.
    否则.线程的名字和Throwable 的堆栈踪迹将被输出到System.err上.

要给所有线程安装一个缺省的处理器用Thread类的静态方法:
    void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandlereh)
获取该缺省的处理器用静态方法:
    Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
注意. 如果你没有安装该缺省处理器. 则处理器为null.

1.5 同步

多个线程同时读写共享的资源. 会互相干扰. 这时就需要同步.

1.5.3 锁对象ReentrantLock

java.util.concurrent.locks.ReentrantLock类
锁类实现了java.util.concurrent.locks.Lock接口.Lock接口的方法包括(部分):
    void lock()  //锁定
    void unlock()  //释放
当一个线程用lock()将某个锁对象锁定后.其它线程如果也想锁定它就会休眠.直到该锁被unlock().
所以ReentrantLock类就可以用来实现对共享资源的独占性访问. 代码通常如下:
    Lock l = new ReentrantLock();
    l.lock();
    try {
        //访问共享资源...
    } finally {
        l.unlock();
    }

1.5.4 条件对象 Condition
如果一个线程用锁定了一个锁进入临界区.却发现还需要满足某个条件才能继续执行.这需要Condition对象.
Condition 是一个接口. 它的方法包括(部分):
    void await() //这会把当前线程放到该"条件对象"的等待集中. 线程一直等到该"条件对象"发出信号.
    void signal() //

//是不是太复杂了??????????????? 以后再看~~

 

 

  


13. 线程 - Java2详解


让一个类的对象可以作为线程有两种办法:

一种是将自己的类声明为java.lang.Thread的子类. 并重写Thread类的run方法. 如:
public class Abc extends Thread {
    public void run() { ... }
}
然后创建Abc的实例并启动:
Abc a = new Abc();
a.start();

另一种办法是使用Runnable接口. Runnable接口只有一个方法 run(). 在自己的类中覆盖这个方法. 如:
public class Abc implements Runnable {
    public void run() { ... }
}
创建线程时先创建一个Runnable对象.再用Runnable对象创建Thread对象并启动. 如:
Abc a = new Abc();
new Thread(a).start();

 

Thread类

public class Thread extends Object implements Runnable{ ... }


构造方法:
    public Thread()
    public Thread(Runnable target)
    public Thread(ThreadGroup group, Runnable target)
    public Thread(String name)
    public Thread(ThreadGroup group, String name)
    public Thread(Runnable target, String name)
    public Thread(ThreadGroup group, Runnable target, String name)
    public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
    其中参数: 
        group是让创建的线程作为某一个线程组的一员. 若没有此参数则相当于使用null.
        target是一个Runnable对象. 启动线程时会调用该对象的run方法.没有此参数则相当于使用null.
        name是新线程的名字. 如果没有此参数则相当于使用一个自动生成的名字如 "Thread-"+n .
        stackSize是指定的线程堆栈的大小. 一般不使用这个参数.


其他方法:
    public static Thread currentThread()  //返回当前线程对象的引用
    public static void yield() //暂停当前线程对象,并执行其他线程。
    public static void sleep(long millis) throws InterruptedException 
    public static void sleep(long millis, int nanos) throws InterruptedException  
        //线程休眠  (毫秒 或 毫秒+纳秒)
        //注意上边sleep中的InterruptedException 异常. 它是线程正在sleep或wait的时候收到中断请求
        //发生的异常. 因为线程在阻塞的时候无法进入run()中的循环里检查"是否有中断请求" . 所以当在线
        //程阻塞时对其进行中断会导致阻塞调用(如sleep函数)抛出InterruptedException 
     public void start()  //使该线程开始执行. Java虚拟机调用该线程的 run 方法.
            //多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
            //如果线程已经启动.抛出 IllegalThreadStateException .
 
    public void run() //Thread 的子类应该重写该方法。
    public final void stop() 
    public final void stop(Throwable obj) //已过时。强迫线程停止执行.
    public void interrupt() 
        //中断线程。由于上边的stop方法过时了.我们无法强制的终止一个线程. 而只能用这个方法来请求一个
        //线程终止. 当线程对象调用这个方法后. 它内部的"中断标志"会被置位. 而该线程的run方法就应该不时
        //的检查这个"中断标志"后来决定如何响应中断请求. 所以想响应中断请求的线程的run方法应该如下:
            public void run() {
                while (!Thread.currentThread().isInterrupted() ) 
                    do more work ;
            }
        //当在线程阻塞时对其进行中断会导致"阻塞调用(如sleep函数)"抛出InterruptedException .
    public static boolean interrupted()
        //测试当前线程是否已经中断。若已经中断则返回true. 该方法将清除线程的中断状态。它是静态方法.
    public boolean isInterrupted()
        //测试线程是否已经中断。线程的中断状态不受该方法的影响(这正是和静态方法interrupted的不同)。
    public void destroy() //已过时
    public final boolean isAlive() //测试线程是否处于活动状态(已经启动且尚未终止).
    public final void suspend() //已过时。 挂起线程.
    public final void resume() //已过时。 该方法只与 suspend() 一起使用,用来重新开始挂起的进程。
    public final void setPriority(int newPriority) //更改线程的优先级。
        //指定的优先级必须在MIN_PRIORITY到MAX_PRIORITY内.否则抛出IllegalArgumentException异常.
        //如果当前活动线程不能修改这个线程则抛出 SecurityException .
        //当某线程创建一个新 Thread 对象时,新线程被设定为创建线程的优先级.
    public final int getPriority() //返回线程的优先级
    public final void setName(String name) //改变线程名称.
    public final String getName() //返回该线程的名称
    public final ThreadGroup getThreadGroup() 
        //返回该线程所属的线程组。 如果该线程已经终止(停止运行),该方法则返回 null
    public static int activeCount() //返回当前线程的线程组中活动线程的数目
    public static int enumerate(Thread[] tarray) 
        //将当前线程的线程组及其子组中的每一个活动线程复制到参数tarray数组中。并返回放入该数组的线程数.
    public int countStackFrames() //已过时。该线程中的堆栈帧数
    public final void join() throws InterruptedException
    public final void join(long millis) throws InterruptedException
    public final void join(long millis,int nanos) throws InterruptedException
        //等待该线程终止.mullis和nanos为等待的最长时间(毫秒/毫秒和纳秒).为0意味着要一直等下去。
    public static void dumpStack() //打印当前线程的堆栈跟踪。该方法仅用于调试。
    public final void setDaemon(boolean on) //将该线程标记为守护线程(参数为ture)或用户线程
                //该方法必须在启动线程前调用。当正在运行的线程都是守护线程时,Java 虚拟机退出
    public final boolean isDaemon() //测试该线程是否为守护线程。
    public final void checkAccess() //定当前运行的线程是否有权修改该线程
    public String toString() //返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
    public ClassLoader getContextClassLoader() //返回该线程的上下文 ClassLoader
    public void setContextClassLoader(ClassLoader cl) //设置该线程的上下文 ClassLoader
    public static boolean holdsLock(Object obj)  //当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true
    public StackTraceElement[] getStackTrace() //返回一个表示该线程堆栈转储的堆栈跟踪元素数组
    public static Map<Thread,StackTraceElement[]> getAllStackTraces()
        //返回所有活动线程的堆栈跟踪的一个映射
    public long getId() //返回该线程的ID。线程 ID 是一个正的 long 数,在创建该线程时生成。
        //线程 ID 是惟一的,并终生不变。线程终止时,该线程 ID 可以被重新使用。
    public Thread.State getState() //返回该线程的状态。 该方法用于监视系统状态,不用于同步控制。
    public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
        //设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序
    public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
        //返回线程由于未捕获到异常而突然终止时调用的默认处理程序。如果返回值为 null,则没有默认处理程序
    public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
        //返回该线程由于未捕获到异常而突然终止时调用的处理程序
    public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
        //设置该线程由于未捕获到异常而突然终止时调用的处理程序。

 

13.10 同步

当多个线程同时访问同一个变量.并至少有一个线程需要修改这个变量时.需要同步.
即给方法加上synchronized 如:
public synchronized void setVar(int x) { myVar = x; }
public synchronized int getVar() { return myVar; }
这样当有一个线程运行到setVar时.Java虚拟机会设置一个条件锁.在这个函数返回前不允许其他线程进入
所有synchronized方法.