您的位置:首页 > 编程语言 > Java开发

Java多线程总结(2) — 线程生命周期中常用方法

2015-12-14 22:19 549 查看

1. 线程的状态回顾

  线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:(原文更好理解,就不翻译了)

New When we create a new Thread object using
new
operator, thread state is New Thread. At this point, thread is not alive and it’s a state internal to Java programming.

Runnable When we call
start()
function on Thread object, it’s state is changed to Runnable and the control is given to Thread scheduler to finish it’s execution. Whether to run this thread instantly or keep it in runnable thread pool before running it depends on the OS implementation of thread scheduler.

Running When thread is executing, it’s state is changed to Running. Thread scheduler picks one of the thread from the runnable thread pool and change it’s state to Running and CPU starts executing this thread. A thread can change state to Runnable, Dead or Blocked from running state depends on time slicing, thread completion of run() method or waiting for some resources like IO,Network,etc.

Blocked/Waiting A thread can be waiting for other thread to finish using thread
join
or it can be waiting for some resources to available, for example producer consumer problem (生产者-消费者问题)or waiter notifier implementation or IO resources, then it’s state is changed to Waiting. Once the thread wait state is over, it’s state is changed to Runnable and it’s moved back to runnable thread pool.

Dead Once the thread finished executing, it’s state is changed to Dead and it’s considered to be not alive.



2. 线程生命周期中常用方法总结

2.1 sleep(long millis)

  运行的线程调用sleep方法进入阻塞状态(不会释放任何对象锁),睡眠完之后继续进入就绪状态,等待线程调度。

[code]public void sleepMethod() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                long start = System.currentTimeMillis();
                Thread.sleep(2000);
                long end = System.currentTimeMillis();
                System.out.println(end -start);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}


  输出结果可能稍微大于2000ms。Thread.sleep() 和线程调度模块交互,将当前线程设置为阻塞状态,等待指定的时间。一旦等待时间结束,线程的状态切换为可执行状态,等待CPU的时间片轮转。所以等待的确切时间取决于操作系统的线程调度机制和定时器的精度。

  注意:

  1. 线程sleep期间,并没有失去此线程所获得的监视器(monitors)和锁(locks)。

  2. 线程sleep期间,任何其他的线程抖可以中断此线程,一旦中断,则抛出InterruptedException.

2.2 join()

  join()可以用来暂停当前线程的执行,让调用join()的线程强制运行完成或执行指定的时间。

public final void join()


public final synchronized void join(long millis)


public final synchronized void join(long millis, int nanos)


  测试案例:

[code]public void joinMethod() {
    Thread thread1 = new Thread(new MyRunnable(), "thread1");
    Thread thread2 = new Thread(new MyRunnable(), "thread2");
    Thread thread3 = new Thread(new MyRunnable(), "thread3");
    thread1.start(); //thread1启动,此时只有thread1和main线程竞争

    try {
        // 等待thread1死亡,即此时main线程处于阻塞状态
        thread1.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("thread1死亡,继续执行...");
    thread2.start();//thread2启动,此时只有thread2和main线程竞争

    try {
        // 等待thread2执行5s或还没执行到5s就死亡
        thread2.join(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("thread2已经执行了5s或已经死亡,继续执行...");
    thread3.start();
    System.out.println("---此后线程thread2和thread3竞争执行---");
    System.out.println("main线程执行完...");
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println("Thread started..." + threadName);
        try {
            for(int i = 0; i < 4; i++) {
                Thread.sleep(4000);
                System.out.println("---->" + threadName + " is running...");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread ended..." + threadName);
    }
}


  执行thread1.join();,main线程将处于阻塞状态,此时只有thread1执行,等待thread1执行完处于死亡状态后,切换到main线程,同理执行thread2.join(5000);,main线程等待thread2执行5s或死亡。注意当执行thread3.start();时,由于此时的thread2并没有执行完,所以此后是thread2、thread3以及main线程竞争。

  运行结果:

    


2.3 interrupt()

  Java中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理中断。当调用interrupt()方法的时候,只是设置了要中断线程的中断状态。我们可以从interrupt()方法来看:

[code]    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();// Just to set the interrupt flag
    }


  最终调用的是
interrupt0()
方法,而
interrupt0()
只是设置了中断标志位,并没有实际中断该线程。

  而此时被中断的线程的可以通过
isInterrupted()
interrupted()
方法判断当前线程的中断状态。看下isInterrupted()方法:

[code]   public boolean isInterrupted() {
       return isInterrupted(false);
   }
   /**
    * Tests if some Thread has been interrupted.  The interrupted state
    * is reset or not based on the value of ClearInterrupted that is
    * passed.
    */
   private native boolean isInterrupted(boolean ClearInterrupted);


  从这个方法可以看出:isInterrupted()方法仅仅是检查了当前线程的中断状态,但是不会清除这个状态。

  看下interrupted()方法:

[code]   public static boolean interrupted() {
       return currentThread().isInterrupted(true);
   }


  说明interrupted()方法检查中断状态的同时,清除了中断标志位。

  调用Thread.sleep()方法的时候,如果当前线程处于中断状态,那么sleep()方法不会执行,同时会清除掉该状态,并且抛出interruptedException异常。

[code]public void interruptMethod() {

    Runnable runnable1 = new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("thread1中检测到被中断!");
                    System.out.println("thread1的中断标志:" + Thread.currentThread().isInterrupted());
                    break; // 自己结束线程
                }
                System.out.println("thread1的中断标志:" + Thread.currentThread().isInterrupted());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    /*
                     *  sleep方法抛出这个异常之后会清除中断状态,所以需要重新设置中断状态
                     *  如果没有重新设置线程为中断状态,则sleep抛出异常后,会reset中断状态,
                     *  因此此案例还是会和main线程竞争
                     */
                    // Thread.currentThread().interrupt();
                    System.out.println("重新设置thread1的中断标志:" + Thread.currentThread().isInterrupted());
                }
            }
        }
    };

    final Thread thread1 = new Thread(runnable1);
    Runnable runnable2 = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
                // 设置thread1的中断状态
                thread1.interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
       Thread thread2 = new Thread(runnable2);

       thread1.start();
       thread2.start();
       try {
        Thread.sleep(6000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("main线程检测thread1的中断标志:" + thread1.isInterrupted());
    System.out.println("main线程检测thread1是否存活:" + thread1.isAlive());
}


  由于thread1中断时,执行Thread.sleep()函数,会抛出InterruptedException,同时reset中断标志,所以thread1仍然处于Running/Runnable状态,会和main线程竞争资源。运行结果如下:

  



  在sleep的catch段中添加
Thread.currentThread().interrupt();
,重新设置中断状态,输出结果:

  


2.4 yield()

  yield有退让的意思,暂停当前正在执行的线程对象,并执行其他线程。注意:这里的其他也包含当前线程。

[code]public void yieldMethod() {
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String threadName = Thread.currentThread().getName();
                for(int i = 1; i < 20; i++) {
                    Thread.sleep(500);
                    if (i == 10) {
                        System.out.println(threadName + " yield!");
                        Thread.currentThread().yield();
                    }
                    System.out.println(threadName + "-->" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    thread1.start();

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String threadName = Thread.currentThread().getName();
                for(int i = 1; i < 20; i++) {
                    Thread.sleep(500);
                    System.out.println(threadName + "----->" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    thread2.start();
}


2.5 wait(), notify() and notifyAll()

wait(): Causes the current thread to wait until another thread invokes the
java.lang.Object.notify()
method or the
java.lang.Object.notifyAll()
method for this object. The current thread must own this object’s monitor.

notify(): Wakes up a single thread that is waiting on this object’s monitor.

notifyAll(): Wakes up all threads that are waiting on this object’s monitor.

  此处简单给出Demo,后面会具体讨论
生产者-消费者
这样的线程通信问题。

[code]/**
 * 线程间传递的消息bean
 */
class Message {
    private String msg;
    public Message(String str){
        this.msg=str;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String str) {
        this.msg=str;
    }
}

class Waiter implements Runnable{
    private Message msg;
    public Waiter(Message m){
        this.msg=m;
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        synchronized (msg) {
            try{
                System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
                /*
                 *  The current thread must own this object's monitor.
                 *  意味着当前线程必须将msg作为监视器
                 *  wait方法,暂时先把对象锁给让出来,给其它持有该锁的对象用,
                 *  使当前线程处于阻塞状态
                 */
                //while(condition) { //注意在实际使用(如进程通信)时,要将wait放在循环当中,而不是if!
                    msg.wait();
                //}
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
            //process the message now
            System.out.println(name+" processed: "+msg.getMsg());
        }
    }
}

class Notifier implements Runnable {

    private Message msg;
    public Notifier(Message msg) {
        this.msg = msg;
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+" started");
        try {
            Thread.sleep(1000);
            synchronized (msg) {
                msg.setMsg(name+" Notifier work done");
                msg.notify();
                // msg.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public void waitNotifyNotifyAllMethod() {
    Message msg = new Message("process it");
    Waiter waiter = new Waiter(msg);
    new Thread(waiter, "waiter").start();

    Waiter waiter1 = new Waiter(msg);
    new Thread(waiter1, "waiter1").start();

    Notifier notifier = new Notifier(msg);
    new Thread(notifier, "notifier").start();
}


  调用notify()时,只唤醒被msg.wait()阻塞的线程中的一个,而另一个线程仍让处于阻塞状态,所以程序进程未结束:

  



  调用notifyAll()时,唤醒被msg.wait()阻塞的所有线程,所以程序进程结束:

  


  转载请注明出处:线程生命周期中常用方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: