精华内容
下载资源
问答
  • Java线程的6种状态及切换(透彻讲解)

    万次阅读 多人点赞 2016-12-24 16:57:03
    Java中线程状态分为6。 1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。 2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,...

    Java中线程的状态分为6种。

    1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
    2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
    3. 阻塞(BLOCKED):表示线程阻塞于锁。
    4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
    5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
    6. 终止(TERMINATED):表示该线程已经执行完毕。

    这6种状态定义在Thread类的State枚举中,可查看源码进行一一对应。

    一、线程的状态图     线程状态图

    二、状态详细说明

    1. 初始状态(NEW)

    实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。

    2.1. 就绪状态(RUNNABLE之READY)

    1. 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
    2. 调用线程的start()方法,此线程进入就绪状态。
    3. 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
    4. 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
    5. 锁池里的线程拿到对象锁后,进入就绪状态。

    2.2. 运行中状态(RUNNABLE之RUNNING)

    线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一的一种方式。

    3. 阻塞状态(BLOCKED)

    阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

    4. 等待(WAITING)

    处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

    5. 超时等待(TIMED_WAITING)

    处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

    6. 终止状态(TERMINATED)

    1. 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
    2. 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

    三、等待队列

    • 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。
    • 与等待队列相关的步骤和图

    1. 线程1获取对象A的锁,正在使用对象A。
    2. 线程1调用对象A的wait()方法。
    3. 线程1释放对象A的锁,并马上进入等待队列。
    4. 锁池里面的对象争抢对象A的锁。
    5. 线程5获得对象A的锁,进入synchronized块,使用对象A。
    6. 线程5调用对象A的notifyAll()方法,唤醒所有线程,所有线程进入同步队列。若线程5调用对象A的notify()方法,则唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入同步队列。
    7. notifyAll()方法所在synchronized结束,线程5释放对象A的锁。
    8. 同步队列的线程争抢对象锁,但线程1什么时候能抢到就不知道了。 

    四、同步队列状态

    • 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入同步队列。简言之,同步队列里面放的都是想争夺对象锁的线程。
    • 当一个线程1被另外一个线程2唤醒时,1线程进入同步队列,去争夺对象锁。
    • 同步队列是在同步的环境下才有的概念,一个对象对应一个同步队列
    • 线程等待时间到了或被notify/notifyAll唤醒后,会进入同步队列竞争锁,如果获得锁,进入RUNNABLE状态,否则进入BLOCKED状态等待获取锁。

    五、几个方法的比较

    1. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
    2. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
    3. thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。
    4. obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
    5. obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
    6. LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。

    六、疑问

    1. 等待队列里许许多多的线程都wait()在一个对象上,此时某一线程调用了对象的notify()方法,那唤醒的到底是哪个线程?随机?队列FIFO?or sth else?Java文档就简单的写了句:选择是任意性的(The choice is arbitrary and occurs at the discretion of the implementation)。
    展开全文
  • 一、线程四种状态 新建(new):处于该状态的时间很短暂。已被分配了必须的系统资源,并执行了初始化。表示有资格获得CPU时间。调度器可以把该线程变为runnable或者blocked状态 就绪(Runnable):这种状态下...

    一、线程的四种状态

    新建(new):处于该状态的时间很短暂。已被分配了必须的系统资源,并执行了初始化。表示有资格获得CPU时间。调度器可以把该线程变为runnable或者blocked状态

    就绪(Runnable):这种状态下只要调度器把时间片分配给线程,线程就能运行。处在这种状态就是可运行可不运行的状态

    阻塞(Bolocked):线程能够运行,但有个条件阻止它的运行。当线程处于阻塞状态时,调度器将会忽略线程,不会分配给线程任何CPU时间(例如sleep)。只有重新进入了就绪状态,才有可能执行操作。

    死亡(Dead):处于死亡状态的线程讲不再是可调度的,并且再也不会得到CPU时间。任务死亡的通常方式是从run()方法返回。

    一个任务进入阻塞状态,可能有如下原因:
    1.sleep
    2.wait(),知道线程得到了notify()或者notifyAll()消息,线程才会进入就绪状态。
    3.任务在等待某个输入/输出完成
    4.线程在试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取了这个锁。

    二、wait和sleep的区别

    1.wait和notify方法定义在Object类中,因此会被所有的类所继承。 这些方法都是final的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。 而sleep方法是在Thread类中是由native修饰的,本地方法。

        public static native void sleep(long l) throws InterruptedException;

     

    2.当线程调用了wait()方法时,它会释放掉对象的锁。
    另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。

    3.正因为wait方法会释放锁,所以调用该方法时,当前的线程必须拥有当前对象的monitor,也即lock,就是锁。要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。

    顺便说说notify(),notify()方法会唤醒一个等待当前对象的锁的线程。 如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)。

    被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。
    被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争。
    且notify方法和wait一样,是需要放在synchronized方法或synchronized块中。

    展开全文
  • 线程创建的四种方式

    万次阅读 多人点赞 2018-03-30 14:34:14
    java中创建线程四种方法以及区别 Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线程...

    java中创建线程的四种方法以及区别

    Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程,如下所示:

    1)继承Thread类创建线程

    2)实现Runnable接口创建线程

    3)使用Callable和Future创建线程

    4)使用线程池例如用Executor框架

    下面让我们分别来看看这四种创建线程的方法。

    ------------------------继承Thread类创建线程---------------------

    通过继承Thread类来创建并启动多线程的一般步骤如下

    1】d定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。

    2】创建Thread子类的实例,也就是创建了线程对象

    3】启动线程,即调用线程的start()方法

    代码实例

    public class MyThread extends Thread{//继承Thread类

      public void run(){

      //重写run方法

      }

    }

    public class Main {

      public static void main(String[] args){

        new MyThread().start();//创建并启动线程

      }

    }

    ------------------------实现Runnable接口创建线程---------------------

    通过实现Runnable接口创建并启动线程一般步骤如下:

    1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体

    2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象

    3】第三部依然是通过调用线程对象的start()方法来启动线程

    代码实例:

    public class MyThread2 implements Runnable {//实现Runnable接口

      public void run(){

      //重写run方法

      }

    }

    public class Main {

      public static void main(String[] args){

        //创建并启动线程

        MyThread2 myThread=new MyThread2();

        Thread thread=new Thread(myThread);

        thread().start();

        //或者    new Thread(new MyThread2()).start();

      }

    }

    ------------------------使用Callable和Future创建线程---------------------

    和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

    》call()方法可以有返回值

    》call()方法可以声明抛出异常

    Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。

    >boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里面关联的Callable任务

    >V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值

    >V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException

    >boolean isDone():若Callable任务完成,返回True

    >boolean isCancelled():如果在Callable任务正常完成前被取消,返回True

    介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:

    1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

    2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

    3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

    4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

    代码实例:

    public static class MyThread3 implements Callable{
    
        @Override
        public Object call() throws Exception {
            return 5;
        }
    }

    public class Main {

      public static void main(String[] args){

       MyThread3 th=new MyThread3();

       //也可以直接使用Lambda表达式创建Callable对象

         //使用FutureTask类来包装Callable对象

       FutureTask<Integer> future=new FutureTask<Integer>(

        (Callable<Integer>)()->{

          return 5;

        }

        );

       new Thread(future,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程

        try{

        System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回

        }catch(Exception e){

        ex.printStackTrace();

       }

      }

    }

    ------------------------使用线程池例如用Executor框架---------------------

    1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象。

        Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。

        Executor接口中之定义了一个方法execute(Runnable command),该方法接收一个Runable实例,它用来执行一个任务,任务即一个实现了Runnable接口的类。ExecutorService接口继承自Executor接口,它提供了更丰富的实现多线程的方法,比如,ExecutorService提供了关闭自己的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以调用ExecutorService的shutdown()方法来平滑地关闭 ExecutorService,调用该方法后,将导致ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭ExecutorService。因此我们一般用该接口来实现和管理多线程。

        ExecutorService的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了shutdown()方法时,便进入关闭状态,此时意味着ExecutorService不再接受新的任务,但它还在执行已经提交了的任务,当素有已经提交了的任务执行完后,便到达终止状态。如果不调用shutdown()方法,ExecutorService会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。

        Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。   

        public static ExecutorService newFixedThreadPool(int nThreads)

        创建固定数目线程的线程池。

        public static ExecutorService newCachedThreadPool()

        创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线   程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

        public static ExecutorService newSingleThreadExecutor()

        创建一个单线程化的Executor。

        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

        创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

        这四种方法都是用的Executors中的ThreadFactory建立的线程,下面就以上四个方法做个比较




    newCachedThreadPool()                                                                                                                                         

    -缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中
    -缓存型池子通常用于执行一些生存期很短的异步型任务
     因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。
    -能reuse的线程,必须是timeout IDLE内的池中线程,缺省     timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
      注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
     


    newFixedThreadPool(int)                                                      

    -newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
    -其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待直到当前的线程中某个线程终止直接被移出池子
    -和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
    -从方法的源代码看,cache池和fixed 池调用的是同一个底层 池,只不过参数不同:
    fixed池线程数固定,并且是0秒IDLE(无IDLE)    
    cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE  

     
    newScheduledThreadPool(int)
    -调度型线程池
    -这个池子里的线程可以按schedule依次delay执行,或周期执行
     
    newSingleThreadExecutor()
    -单例线程,任意时间池中只能有一个线程
    -用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE)

     

        一般来说,CachedTheadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor的首选,只有当这种方式会引发问题时(比如需要大量长时间面向连接的线程时),才需要考虑用FixedThreadPool。(该段话摘自《Thinking in Java》第四版)

                             

    Executor执行Runnable任务

        通过Executors的以上四个静态工厂方法获得 ExecutorService实例,而后调用该实例的execute(Runnable command)方法即可。一旦Runnable任务传递到execute()方法,该方法便会自动在一个线程上

    [java] view pl
     
    import java.util.concurrent.ExecutorService;   
    import java.util.concurrent.Executors;   
      
    public class TestCachedThreadPool{   
        public static void main(String[] args){   
            ExecutorService executorService = Executors.newCachedThreadPool();   
    //      ExecutorService executorService = Executors.newFixedThreadPool(5);  
    //      ExecutorService executorService = Executors.newSingleThreadExecutor();  
            for (int i = 0; i < 5; i++){   
                executorService.execute(new TestRunnable());   
                System.out.println("************* a" + i + " *************");   
            }   
            executorService.shutdown();   
        }   
    }   
      
    class TestRunnable implements Runnable{   
        public void run(){   
            System.out.println(Thread.currentThread().getName() + "线程被调用了。");   
        }   
    }  

       某次执行后的结果如下:

       从结果中可以看出,pool-1-thread-1和pool-1-thread-2均被调用了两次,这是随机的,execute会首先在线程池中选择一个已有空闲线程来执行任务,如果线程池中没有空闲线程,它便会创建一个新的线程来执行任务。

    Executor执行Callable任务

        在Java 5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的submit(Callable<T> task) 方法来执行,并且返回一个 <T>Future<T>,是表示任务等待完成的 Future。

        Callable接口类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常而Callable又返回结果,而且当获取返回结果时可能会抛出异常。Callable中的call()方法类似Runnable的run()方法,区别同样是有返回值,后者没有。

        当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。

        下面给出一个Executor执行Callable任务的示例代码:

    import java.util.ArrayList;   
    import java.util.List;   
    import java.util.concurrent.*;   
      
    public class CallableDemo{   
        public static void main(String[] args){   
            ExecutorService executorService = Executors.newCachedThreadPool();   
            List<Future<String>> resultList = new ArrayList<Future<String>>();   
      
            //创建10个任务并执行   
            for (int i = 0; i < 10; i++){   
                //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中   
                Future<String> future = executorService.submit(new TaskWithResult(i));   
                //将任务执行结果存储到List中   
                resultList.add(future);   
            }   
      
            //遍历任务的结果   
            for (Future<String> fs : resultList){   
                    try{   
                        while(!fs.isDone);//Future返回如果没有完成,则一直循环等待,直到Future返回完成  
                        System.out.println(fs.get());     //打印各个线程(任务)执行的结果   
                    }catch(InterruptedException e){   
                        e.printStackTrace();   
                    }catch(ExecutionException e){   
                        e.printStackTrace();   
                    }finally{   
                        //启动一次顺序关闭,执行以前提交的任务,但不接受新任务  
                        executorService.shutdown();   
                    }   
            }   
        }   
    }   
      
      
    class TaskWithResult implements Callable<String>{   
        private int id;   
      
        public TaskWithResult(int id){   
            this.id = id;   
        }   
      
        /**  
         * 任务的具体过程,一旦任务传给ExecutorService的submit方法, 
         * 则该方法自动在一个线程上执行 
         */   
        public String call() throws Exception {  
            System.out.println("call()方法被自动调用!!!    " + Thread.currentThread().getName());   
            //该返回结果将被Future的get方法得到  
            return "call()方法被自动调用,任务返回的结果是:" + id + "    " + Thread.currentThread().getName();   
        }   
    }  

        某次执行结果如下:

       

        从结果中可以同样可以看出,submit也是首先选择空闲线程来执行任务,如果没有,才会创建新的线程来执行任务。另外,需要注意:如果Future的返回尚未完成,则get()方法会阻塞等待,直到Future完成返回,可以通过调用isDone()方法判断Future是否完成了返回。

    自定义线程池

        自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池,用该类很容易实现自定义的线程池,这里先贴上示例程序:

    import java.util.concurrent.ArrayBlockingQueue;   
    import java.util.concurrent.BlockingQueue;   
    import java.util.concurrent.ThreadPoolExecutor;   
    import java.util.concurrent.TimeUnit;   
      
    public class ThreadPoolTest{   
        public static void main(String[] args){   
            //创建等待队列   
            BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20);   
            //创建线程池,池中保存的线程数为3,允许的最大线程数为5  
            ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,50,TimeUnit.MILLISECONDS,bqueue);   
            //创建七个任务   
            Runnable t1 = new MyThread();   
            Runnable t2 = new MyThread();   
            Runnable t3 = new MyThread();   
            Runnable t4 = new MyThread();   
            Runnable t5 = new MyThread();   
            Runnable t6 = new MyThread();   
            Runnable t7 = new MyThread();   
            //每个任务会在一个线程上执行  
            pool.execute(t1);   
            pool.execute(t2);   
            pool.execute(t3);   
            pool.execute(t4);   
            pool.execute(t5);   
            pool.execute(t6);   
            pool.execute(t7);   
            //关闭线程池   
            pool.shutdown();   
        }   
    }   
      
    class MyThread implements Runnable{   
        @Override   
        public void run(){   
            System.out.println(Thread.currentThread().getName() + "正在执行。。。");   
            try{   
                Thread.sleep(100);   
            }catch(InterruptedException e){   
                e.printStackTrace();   
            }   
        }   
    }  

      运行结果如下:

        从结果中可以看出,七个任务是在线程池的三个线程上执行的。这里简要说明下用到的ThreadPoolExecuror类的构造方法中各个参数的含义。   

    public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long         keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue)

    corePoolSize:线程池中所保存的线程数,包括空闲线程。

    maximumPoolSize:池中允许的最大线程数。

    keepAliveTime:当线程数大于核心数时,该参数为所有的任务终止前,多余的空闲线程等待新任务的最长时间。

    unit:等待时间的单位。

    workQueue:任务执行前保存任务的队列,仅保存由execute方法提交的Runnable任务。

    --------------------------------------四种创建线程方法对比--------------------------------------

    实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,后者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:

    1、线程只是实现Runnable或实现Callable接口,还可以继承其他类。

    2、这种方式下,多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。

    3、但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。

    4、继承Thread类的线程类不能再继承其他父类(Java单继承决定)。

    5、前三种的线程如果创建关闭频繁会消耗系统资源影响性能,而使用线程池可以不用线程的时候放回线程池,用的时候再从线程池取,项目开发中主要使用线程池

    注:在前三种中一般推荐采用实现接口的方式来创建多线程

    创建线程本质只有1种,即创建Thread类,以上的所谓创建方式其实是实现run方法的方式:

    实现run方法的方式分为两类,你所看到的其他的都是对这两类的封装:

    1、实现runnable接口的run方法,并把runnable实例作为target对象,传给thread类,最终调用target.run

    2、继承Thread类,重写Thread的run方法,Thread.start会执行run方法

    展开全文
  • 线程的5种状态详解

    万次阅读 多人点赞 2018-08-23 11:02:42
    Java中的线程的生命周期大体可分为5种状态。 1.新建(NEW):新创建了一个线程对象。 2.可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态线程位于可运行线程池中,...

    Java中的线程的生命周期大体可分为5种状态。

     

    1. 新建(NEW):新创建了一个线程对象。

    2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

    3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
    4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种: 

    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

    5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

     

     

    一.线程的状态图

         线程状态转换

     

    二.初始状态

    1. 实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态

    三.可运行状态

    1. 可运行状态只是说你资格运行,调度程序没有挑选到你,你就永远是可运行状态。
    2. 调用线程的start()方法,此线程进入可运行状态。
    3. 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态。
    4. 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入可运行状态。
    5. 锁池里的线程拿到对象锁后,进入可运行状态。

    四.运行状态

    1. 线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

    五.死亡状态

    1. 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。
    2. 在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

    六.阻塞状态

    1. 当前线程T调用Thread.sleep()方法,当前线程进入阻塞状态。
    2. 运行在当前线程里的其它线程t2调用join()方法,当前线程进入阻塞状态。
    3. 等待用户输入的时候,当前线程进入阻塞状态。

    七.等待队列(本是Object里的方法,但影响了线程)

    1. 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。
    2. 与等待队列相关的步骤和图
    • 线程1获取对象A的锁,正在使用对象A。
    • 线程1调用对象A的wait()方法。
    • 线程1释放对象A的锁,并马上进入等待队列。
    • 锁池里面的对象争抢对象A的锁。
    • 线程5获得对象A的锁,进入synchronized块,使用对象A。
    • 线程5调用对象A的notifyAll()方法,唤醒所有线程,所有线程进入锁池。||||| 线程5调用对象A的notify()方法,唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入锁池。
    • notifyAll()方法所在synchronized结束,线程5释放对象A的锁。
    • 锁池里面的线程争抢对象锁,但线程1什么时候能抢到就不知道了。||||| 原本锁池+第6步被唤醒的线程一起争抢对象锁。多线程等待队列

     

    八.锁池状态

    1. 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入锁池状态。简言之,锁池里面放的都是想争夺对象锁的线程。
    2. 当一个线程1被另外一个线程2唤醒时,1线程进入锁池状态,去争夺对象锁。
    3. 锁池是在同步的环境下才有的概念,一个对象对应一个锁池。

    九.几个方法的比较

    1. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入阻塞,但不释放对象锁,millis后线程自动苏醒进入可运行状态。作用:给其它线程执行机会的最佳方式。
    2. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。
    3. t.join()/t.join(long millis),当前线程里调用其它线程1的join方法,当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。
    4. obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒。
    5. obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

     

    线程状态:

        

    线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。


        1.新建状态(New): 
            当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

         2.就绪状态(Runnable)

            一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

            处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。


        3.运行状态(Running)

            当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.

        4. 阻塞状态(Blocked)

            线程运行过程中,可能由于各种原因进入阻塞状态:
            1>线程通过调用sleep方法进入睡眠状态;
            2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
            3>线程试图得到一个锁,而该锁正被其他线程持有;
            4>线程在等待某个触发条件;
            ......           
            所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

        5. 死亡状态(Dead)

            有两个原因会导致线程死亡:
             1) run方法正常退出而自然死亡,
             2) 一个未捕获的异常终止了run方法而使线程猝死。
            为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

     

    展开全文
  • [线程学习]线程四种状态

    千次阅读 2006-09-29 16:16:00
    线程四种状态,任何一个线程肯定处于这四种状态中的一种:产生(New),可执行(Runnable),死亡(Dead),停滞(Blocked)。1、产生(New):线程对象已经产生,但尚未被启动,所以无法执行。如通过new产生了一...
  • 线程四种状态

    千次阅读 2007-11-11 01:24:00
    1. 新状态线程已被创建但尚未执行(start() 尚未被调用)。2. 可执行状态线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。3. 死亡状态:正常情况下 run() 返回使得...
  • Java线程线程的五种状态

    千次阅读 2016-10-11 14:17:48
    线程的 5 中状态 1. New 新建状态 当程序使用 new 关键字创建了一个线程后,该线程就处于新建状态,此时线程还未启劢, 当线程对象调用 start()方法时,线程启劢,迚入 Runnable 状态 2. Runnable 可运行(就绪...
  • 详细介绍了Java线程的6种状态,以及状态之间的转换。
  • 线程5种状态及常见问题

    千次阅读 2017-10-19 18:22:05
    线程的5种状态:  每个 Java 程序都有一个缺省的主线程,对于 Java 应用程序,主线程是 main()方法执行的线索; 对于 Applet 程序,主线程是指挥浏览器加载并执行 Java Applet 程序的线索。要想...
  • Java 线程的七种状态

    千次阅读 2019-09-26 19:31:10
    Java 线程状态转换图 Java 线程状态 在 Java 线程的生命周期中,按复杂的并发场景考虑,可将 Java 线程的状态分为七,分别是:新建(New),可运行(Runnable 或 Ready),运行(Running),无限期等待(Waiting...
  • Java线程的6种状态

    千次阅读 2019-05-26 15:46:56
    Java的线程大致有6种状态: NEW: 新建状态,new了一个线程。 Runable: 可执行状态线程调用了start()或run()方法,或者等待结束,都会进入Runable状态,在操作系统或处理器而言,java的Runable状态可能是他们...
  • 线程的六种状态及其状态的转换

    万次阅读 2018-10-04 18:15:26
    1.线程自身信息 线程运行的过程会产生很多信息,这些信息都保... 线程的优先级:getPriority,线程优先级从1-10,其中数字越大表示优先级别越高,同时获得JVM调度执行的可能性越大,JDK内置了三常见的状态: //...
  • 守护线程及六种线程状态简介

    千次阅读 热门讨论 2020-03-03 18:55:10
    守护线程及六种线程状态简介 1. 守护线程 守护线程一般用于: 1. 自动下载 2. 操作日志 3. 操作监控 方法是通过线程对象 setDeamon(boolean flag); true为守护线程 false缺省属性,正常线程 2. 线程状态 ...
  • 线程的几种状态

    千次阅读 2018-08-15 22:24:57
    1. 线程的几种状态 初始状态:是指线程刚刚创建,尚未启动,还没有调start()方法 运行状态:分为就绪和运行中状态 就绪:线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态线程...
  • Java多线程03_线程状态、优先级、用户线程和守护线程 线程方法: setPriority() 更改线程优先级 static void sleep() 线程休眠 void join() 插队 static void yield() 礼让 void interrupt() 中断...
  • Python 线程的五种状态、守护线程

    千次阅读 2019-08-20 11:07:49
    一、线程的五种状态 大多数线程的生命周期经历有 新建、就绪、运行、阻塞、死亡 这五种状态,对于多线程来说,一个线程不可能始终霸占着CPU,CPU需要在不同线程之间切换,于是线程状态也会随之改变。 1.新建 当...
  • 简述线程的几种状态以及状态之间的转换

    千次阅读 多人点赞 2019-08-23 21:38:24
    线程有以下几种状态 1、new 新建状态(此时线程刚被new出来,还没执行start()方法) 2、Runable运行状态,有执行资格又有执行权,但它可能正在等待来自操作系统的其他资源,例如处理器。 3、Blocked 阻塞状态,也...
  • 线程有哪些状态

    千次阅读 2019-05-14 12:16:58
    线程有哪些状态? 新建(NEW):新建线程对象,未调用 start 方法 可运行(RUNNABLE):线程对象创建后,被调用 start 方法。此状态线程位于可运行线程池中,等待获取 CPU 的使用权 运行中(RUNNING):线程获取了 ...
  • java多线程的几种状态

    万次阅读 2018-09-04 13:00:38
    Java线程既可以创建,也可以销毁,所以线程是有生命周期的,而线程的生命周期就可以用线程的六种状态来描述。 从Thread类的源码中可以看出线程一共有6种状态: 这六种状态分别是: 1. New:初始状态线程被创建...
  • 线程的几种状态转换

    万次阅读 多人点赞 2016-09-17 20:49:17
    线程一共有以下几种状态: 1、新建状态(New):新创建了一个线程对象。 2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态线程位于“可运行线程池”中,变得可运行,只等待获取...
  • Java线程到底有几种状态

    千次阅读 2019-08-19 23:14:06
    前言 很多文章说Java线程有五状态:NEW(新建)、RUNNABLE(就绪)、RUNNING(运行)、BLOCKED(阻塞)...线程状态 Java线程到底有几状态,其实只要打开一下JDK源码,看一下java.lang.Thread类就知道了,java.l...
  • 介绍Java8中Thread(线程)定义的6种状态,并和传统线程模型的三/五种状态进行对比
  • 线程状态:   线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。 1.新建状态(New): 当用new操作符创建一个线程时, 例如new Thread(r),线程还没有...
  • 02. 线程的五种状态

    千次阅读 2019-09-01 18:52:47
    线程启动后, 不可能一直霸占着CPU 独自运行, CPU 需要在多个线程之间切换, 因此线程状态也就会在运行, 就绪之间来回切换. 1.1 线程的五状态之间的转换 新建状态: 通过new 创建线程后, 线程便进入了新建状态 ...
  • 线程有六种状态: new(新创建),runnable(可运行),blocked(被阻塞),waiting(等待),timed waiting(计时等待),terminated(被终止) 几方法的比较: void interrupt() 向线程发送中断请求。线程的中断状态将被置为...
  • 线程及线程池的五种状态

    千次阅读 2019-06-21 16:14:27
    线程的5种状态 线程可以有如下5种状态:New 、Runnable 、Running 、Blocked 、Dead 状态之间的转换如图: 1.New (新创建) 当用new操作符创建一个线程时,如new Thread®,该线程还没有开始运行。这意外这它的状态...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,050,324
精华内容 420,129
关键字:

线程的四种状态