Java Thread及其synchronized,wait,sleep,join,yeid,interrupt

浪子不回头ぞ 提交于 2019-12-02 09:05:48

Java SE7 API - Thread:

 http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#yield%28%29

参考资料:http://blog.csdn.net/lqqmisslll/article/details/54208491

 

一、线程的简介

      当JVM启动的时候, 通常会有一个独立的非守护线程(也就是类中的main方法所在的线程).JVM会继续运行,除非发生以下情况:

      Runtime类的exit()方法被调用,并且安全管理者允许退出发生。

      所有非守护线程都已经死了,不管是从run方法中返回的还是因为run方法中抛出了异常。

      注意:当所有非守护线程都执行结束(包括主线程),那么守护线程也会退出。因为守护线程是无法脱离非守护线程而独自存在的。

二、创建线程有两种方式:

        方法1:声明一个类作为Thread的子类(extends Thread),子类重写(override)Thread类的run()方法。子类的实例可以被分配和start。        

    //比如:该线程用来计算比指定起始值大的素数。
    class PrimeThread extends Thread 
    {
         long minPrime;
         PrimeThread(long minPrime) 
         {
             this.minPrime = minPrime;
         }
         public void run() 
         {
             // compute primes larger than minPrime
              . . .
         }
     }
     //以下代码用来创建和启动线程PrimeThread 
     PrimeThread p = new PrimeThread(143);
     p.start();

  方法2: 声明一个实现Runnable接口的类(implements Runnable),该类实现run()方法。该类的实例可以被分配,或者在创建Thread时作为一个参数,并且start.

    class PrimeRun implements Runnable 
    {
         long minPrime;
         PrimeRun(long minPrime) 
         {
             this.minPrime = minPrime;
         }

         public void run() 
         {
             // compute primes larger than minPrime
              . . .
         }
     }
     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();

        继承Thread与实现Runnable的区别:

  1. 实现 Runnable 大多数情况下是比继承 Thread 更好的方法。

  2. 1. Java 只支持单继承,所以你继承了 Thread 的话,其他的类你就不能继承了。

  3. 2. 如果实现 Runnable 接口,多线程可以直接将你的工作并发处理(直接使用 start)。而继承 Thread 不能直接进行多线程工作,你得为你的每一部分工作都定义一个线程。

  4. 3. 其他的一些API,接口都是 Runnable 类型的。比如 Java 内置的线程池 API ExcutorService 等。

三、线程的状态 
线程有四种状态,任何一个线程肯定处于这四种状态中的一种: 
    1)产生(New):线程对象已经产生,但尚未被启动,所以无法执行。如通过new产生了一个线程对象后没对它调用start()函数之前。 
    2)可执行(Runnable):每个支持多线程的系统都有一个排程器,排程器会从线程池中选择一个线程并启动它。当一个线程处于可执行状态时,表示它可能正处于线程池中等待排排程器启动它;也可能它已正在执行。如执行了一个线程对象的start()方法后,线程就处于可执行状态,但显而易见的是此时线程不一定正在执行中。 
    3)停滞(Blocked):当一个线程处于停滞状态时,系统排程器就会忽略它,不对它进行排程。当处于停滞状态的线程重新回到可执行状态时,它有可能重新执行。如通过对一个线程调用wait()函数后,线程就进入停滞状态,只有当两次对该线程调用notify或notifyAll后它才能两次回到可执行状态。

   4)死亡(Dead):当一个线程正常结束,它便处于死亡状态。如一个线程的run()函数执行完毕后线程就进入死亡状态。

三、Thread的方法:

static intactiveCount() Returns an estimate of the number of active threads in the current thread's thread group and its subgroups.
StringgetName() Returns this thread's name.
intgetPriority() Returns this thread's priority.
Thread.State getState() Returns the state of this thread.
booleanisAlive() Tests if this thread is alive.
booleanisDaemon() Tests if this thread is a daemon thread.
booleanisInterrupted() Tests whether this thread has been interrupted.

    最常用的方法

void join() Waits for this thread to die.
void join(long millis) Waits at most millis milliseconds for this thread to die.
void join(long millis, int nanos) Waits at most millis milliseconds plus nanos nanoseconds for this thread to die.
void run() If this thread was constructed using a separate Runnable run object, then that Runnable object's run method is called; otherwise, this method does nothing and returns.
void setDaemon(boolean on) Marks this thread as either a daemon thread or a user thread.
static void sleep(long millis) Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.
static void sleep(long millis, int nanos) Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds plus the specified number of nanoseconds, subject to the precision and accuracy of system timers and schedulers.
void start() Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.
static void yield() A hint to the scheduler that the current thread is willing to yield its current use of a processor.
void interrupt()

Interrupts this thread.

If this thread is blocked in an invocation of the wait()wait(long), or wait(long, int) methods of the Object class, or of the join()join(long)join(long, int)sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive aClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector'swakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

从java.long.Object继承来的方法:

‍ ‍ ‍ ‍

protected voidfinalize() Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
void notify() Wakes up a single thread that is waiting on this object's monitor.
void notifyAll() Wakes up all threads that are waiting on this object's monitor.
void wait() Causes the current thread to wait until another thread invokes the notify() method or the notifyAll()method for this object.
void wait(long timeout) Causes the current thread to wait until either another thread invokes the notify() method or thenotifyAll() method for this object, or a specified amount of time has elapsed.
void wait(long timeout, int nanos) Causes the current thread to wait until another thread invokes the notify() method or the notifyAll()method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.

注意事项

1.sleep-wait-yield区别

    sleep是Thread类中的一个静态方法,在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响,如果指定睡眠的时间到达,则从阻塞状态转变成就绪状态,等待运行。 
    yield只是使当前线程放弃执行权(重新回到可执行状态),让其它同一优先级的线程先运行,所以执行yield()的线程有可能进入到可执行状态后马上又被执行。 

    wait是Object类中定义的方法,与notify/notifyAll()在一起成对使用,提供线程间的等待-通知机制。 

2.使用wait-notify 
    (1)调用notify是随机的唤醒某一thread.而notifyAll则是唤醒所有等待的线程, 但只有一个线程可以在唤醒后lock object monitor,所以, notifyAll操作也是有利弊的. 
    (2)wait、notify、notifyAll必须在synchronized修饰的代码块中执行,否则会在运行的时候抛出IllegalMonitorStateException 异常 .

    (3)在循环语句wait的时候一定要设定循环的条件(while(flag))--这样能够避免wait开始之前,线程所需的条件已经被其他线程提供了却依然开始此线程wait导致的时间消耗。同时,这种办法还能够保证你的代码不被虚假的信息唤醒。 
    (4)总是要保证在调用notify和notifyAll之前,能够提供符合线程退出等待的条件。否则会出现即使线程接收到通知信息,却不能退出循环等待的情况。 

3.join()方法 
  join方法是使当前线程阻塞,直到所引用的线程结束才激活. 

 

四、实现线程同步的方法

1.有synchronized关键字的同步方法
    synchronized有四种使用方式: 
         synchronized method(){} 
      synchronized (obj) 
      static synchronized method(){} 
      synchronized(classname.class) 

   前面两个使用的锁是对象monitor,后面两者使用的是类monitor,都可以实现互斥访问。 
一个对象只有一个对象monitor,一个类也只有一个类monitor。静态方法使用类monitor进行同步,而普通方法则使用对象monitor进行同步。

2.使用lock()与unlock()之间的同步块。

代码示例:

  1.通过setDaemon(true)将鲜橙设置为守护线程,以及守护线程的特点。

public class Counter
{
    public AtomicInteger  inc = new AtomicInteger();
    public void increase()
    {
        inc.getAndIncrement();
    }
    public static void main(String[] args) throws InterruptedException
    {
        final Counter test = new Counter();
        for (int i = 0; i < 10; i++)
        {
            Thread t = new Thread()
            {
                @Override
                public void run()
                {
                    for (int j = 0; j < 10; j++)
                    {
                        test.increase();
                        System.out.println(Thread.currentThread().getName() + "----" + test.inc);
                    }
                };
            };
//            t.setDaemon(true);
            t.start();            
        }       
        
        while (Thread.activeCount() > 1)//保证前面的线程都执行完
        {
//            Thread.yield();
//            Thread.sleep(1);
//            System.out.println(Thread.currentThread().getName()+"-yield");
        }        
        System.out.println(test.inc);
        System.out.println("Thread.activeCount()="+Thread.activeCount());
    }
}

   通过启用注释掉的部分可以得到不同的结果。

   2. 使用join()控制线程执行顺序:http://my.oschina.net/liuyuanyuangogo/blog/315927

   3. 使用interrupt()方法控制线程的停止:

/**
 *
 * @author Yuanyuan
 * 终止线程的方法
 * stop()方法不安全,已启用。
 * 如何终止线程?
 * 终止线程的方法只有一个,就是让run()方法运行结束.
 * 
 * 开启多线程运行时,运行代码通常是循环结构。
 * 只要控制住循环,就可以让run方法结束,也就是该线程结束。(通常会用一个标记变量来控制)
 * 
 * 特殊情况,
 * 当线程处于冻结状态。就不会读取到标记,那线程就无法结束。
 *  
 * 当没有指定的方式让冻结的线程回复到运行状态,这是需要对冻结进行清除。
 * 强制让线程恢复到运行中来,这样就可以操作标记让线程结束。
 * Thread类提供了方法interrupt()
 * 
 */
class StopThread extends Thread
{
    private boolean flag = true;
    @Override
    public synchronized  void run()
    {
        while(flag)
        {
            try
            {
                this.wait();
            } catch (InterruptedException ex)
            {
                System.out.println(Thread.currentThread().getName()+"---Exception");
                flag = false;
            }
        }
        System.out.println(Thread.currentThread().getName()+"---run");
    }
}
public class ThreadDemo
{
    public static void main(String[] args)
    {
        Thread t1 = new StopThread();
        Thread t2 = new StopThread();
        t1.start();
        t2.start();
        for(int i=0;i<10;i++)
        {
            if(i==5)
            {
                t1.interrupt();//
                t2.interrupt();
            }
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }        
    }
}

举例说明Thread与Runnable的区别:

(以下转自:http://mars914.iteye.com/blog/1508429

 在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程。当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口,现在看一下这两种方式实现的两种结果。

  1. package com.threadtest;  
    class MyThread extends Thread
    {
        private int ticket = 10;  
        private String name;  
        public MyThread(String name)
       {  
            this.name =name;  
        }  
        public void run()
       {  
            for(int i =0;i<500;i++)
            {  
                if(this.ticket>0)
                {  
                    System.out.println(this.name+"卖票---->"+(this.ticket--));  
                }  
            }  
        }  
    }  
    public class ThreadDemo {       
        public static void main(String[] args) {  
            MyThread mt1= new MyThread("一号窗口");  
            MyThread mt2= new MyThread("二号窗口");  
            MyThread mt3= new MyThread("三号窗口");  
            mt1.start();  
            mt2.start();  
            mt3.start();  
        }  
    }

运行结果如下:

  1. 一号窗口卖票---->10  
    一号窗口卖票---->9  
    二号窗口卖票---->10  
    一号窗口卖票---->8  
    一号窗口卖票---->7  
    一号窗口卖票---->6  
    三号窗口卖票---->10  
    一号窗口卖票---->5  
    一号窗口卖票---->4  
    一号窗口卖票---->3  
    一号窗口卖票---->2  
    一号窗口卖票---->1  
    二号窗口卖票---->9  
    二号窗口卖票---->8  
    三号窗口卖票---->9  
    三号窗口卖票---->8  
    三号窗口卖票---->7  
    三号窗口卖票---->6  
    三号窗口卖票---->5  
    三号窗口卖票---->4  
    三号窗口卖票---->3  
    三号窗口卖票---->2  
    三号窗口卖票---->1  
    二号窗口卖票---->7  
    二号窗口卖票---->6  
    二号窗口卖票---->5  
    二号窗口卖票---->4  
    二号窗口卖票---->3  
    二号窗口卖票---->2  
    二号窗口卖票---->1

 

通过实现Runnable接口的代码如下:

  1. class MyThread1 implements Runnable{  
        private int ticket =10;  
        private String name;  
        public void run(){  
            for(int i =0;i<500;i++){  
                if(this.ticket>0){  
                    System.out.println(Thread.currentThread().getName()
                    +"卖票---->"+(this.ticket--));  
                }  
            }  
        }  
    }  
    public class RunnableDemo 
    {        
        public static void main(String[] args)  
        {      
            // TODO Auto-generated method stub  
            //设计三个线程  
             MyThread1 mt = new MyThread1();  
             Thread t1 = new Thread(mt,"一号窗口");  
             Thread t2 = new Thread(mt,"二号窗口");  
             Thread t3 = new Thread(mt,"三号窗口");  
    //         MyThread1 mt2 = new MyThread1();  
    //         MyThread1 mt3 = new MyThread1();  
             t1.start();  
             t2.start();  
             t3.start();  
        }  
      
    }

 运行结果如下:

  1. 一号窗口卖票---->10  
    三号窗口卖票---->9  
    三号窗口卖票---->7  
    三号窗口卖票---->5  
    三号窗口卖票---->4  
    三号窗口卖票---->3  
    三号窗口卖票---->2  
    三号窗口卖票---->1  
    一号窗口卖票---->8  
    二号窗口卖票---->6

    为什么会出现这种结果呐。我们不妨做个比喻,其实刚的程序,

继承Thread类的,我们相当于拿出三件事即三个卖票10张的任务分别分给三个窗口,他们各做各的事各卖各的票各完成各的任务,因为MyThread继承Thread类,所以在new MyThread的时候在创建三个对象的同时创建了三个线程;

实现Runnable的, 相当于是拿出一个卖票10张得任务给三个人去共同完成,new MyThread相当于创建一个任务,然后实例化三个Thread,创建三个线程即安排三个窗口去执行。

用图表示如下:

    在我们刚接触的时候可能会迷糊继承Thread类和实现Runnable接口实现多线程,其实在接触后我们会发现这完全是两个不同的实现多线程,一个是多个线程分别完成自己的任务,一个是多个线程共同完成一个任务。

    其实在实现一个任务用多个线程来做也可以用继承Thread类来实现只是比较麻烦,一般我们用实现Runnable接口来实现,简洁明了。      

    大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类(Thread)创建子类。

 

Difference between Thread vs Runnable interface in Java

   Thread vs Runnable  in Java  is always been a confusing decision for beginner s    in  java.  Thread in Java  seems easy in comparison of Runnable because you just deal with one class  java.lang.Thread  while in case of using Runnable to implement Thread you need to deal with both Thread and Runnable two classes. though decision of using Runnable or Thread should be taken considering differences between Runnable and Thread and pros and cons of both approaches. This is also a very  popular thread interview questions  and most of interviewer are really interested to know what is your point of view while choosing Thread vs Runnable or opposite. In this java article we will try to point out some differences between Thread and Runnable in Java which will help you to take an informed  decision. 

    Thread vs Runnable in Java

Here are some of my thoughts on whether I should use Thread or Runnable for implementing task in Java, though you have another choice as " Callable " for implementing thread which we will discuss later.

‍1)  Java doesn't support multiple inheritance , which means you can only extend one class in Java so on c e you extend ed  Thread class you lost your chance and   can not extend or inherit another  class in Java .‍

2) In Object oriented programming extending a class generally means adding new functionality, modifying or improving behaviors. If we are not making any modification on Thread than use Runnable interface instead.

3) Runnable interface represent a Task which can be executed by either plain  Thread  or  Executors  or any other means. so logical separation of Task as   Runnable  than Thread is good design decision.

4) Separating task as Runnable means we can reuse the task and also has liberty to execute it from different means. since you can not restart a Thread once it completes. again Runnable vs Thread for task, Runnable is winner.

5) Java designer recognizes this and that's why Executors accept Runnable as Task and they have worker thread which executes those task.

    6) Inheriting all Thread methods are additional overhead just for representing a Task which can can be done easily with Runnable.

    These were some of notable difference between Thread and Runnable in Java, if you know any other differences on Thread vs Runnable than please share it   via comments. I personally use Runnable over Thread for this scenario and recommends to use Runnable or Callable interface based on your requirement.

    Some more Java Tutorials you may like

    Why Wait and Notify in Java is called from Synchronized context

    Difference between Comparator and Comparable in Java

    Difference between Wait, Sleep and yield in Java

    How to Stop Thread in Java with Code Example

    How to sort ArrayList in Java with Example

    Why Main is static in Java

    Difference between JVM, JRE and JDK in Java

 

Read more: 

http://javarevisited.blogspot.com/2012/01/difference-thread-vs-runnable-interface.html#ixzz3Ctqfnhpz 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!