当前位置: 首页 > Linux

Java线程的六种状态及切换的划分

时间:2023-04-06 19:51:10 Linux

Thread线程状态:Java线程有6种状态。Initial(NEW):新创建了一个线程对象,但是还没有调用start()方法。运行中(RUNNABLE):Java线程中的就绪(ready)和运行(running)两种状态,一般称为“运行中”。线程对象创建后,其他线程(如主线程)调用该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获得CPU使用权。此时,它处于就绪状态(ready)。处于就绪状态的线程在获得CPU时间片后变为运行状态(running)。3.阻塞(BLOCKED):表示线程阻塞在锁上。4.等待(WAITING):进入该状态的线程需要等待其他线程采取一些特定的动作(通知或中断)。5、超时等待(TIMED_WAITING):这个状态与WAITING不同,它可以在指定的时间后自行返回。终止(TERMINATED):表示线程已经执行完毕。这6种状态定义在Thread类的State枚举中,可以查看源码一一对应。1.线程状态图2.状态详细描述2.1.Initialstate(NEW)实现Runnable接口,继承Thread得到一个线程类。当一个new实例出来时,线程进入初始状态。2.2.就绪状态(READYofRUNNABLE)就绪状态只是意味着你有资格运行,如果调度器(Cpu)不接你,你将一直处于就绪状态。调用线程的start()方法,线程进入就绪状态。当前线程sleep()方法结束,其他线程join()结束,等待用户输入,一个线程拿到对象锁,这些线程也会进入就绪状态。当当前线程的时间片用完后,调用当前线程的yield()方法,当前线程进入就绪状态。锁池中的线程获得对象锁后,进入就绪状态。2.3.运行状态(RUNNINGofRUNNABLE)当线程处于该状态时,线程调度器从runnablepool中选择一个线程作为当前线程。这也是线程进入运行状态的必经之路。2.4.阻塞状态(BLOCKED)阻塞状态是线程在进入由synchronized关键字修饰的方法或代码块(获取锁)时被阻塞的状态。2.5.等待(WAITING)处于这种状态的线程不会被分配CPU执行时间,它们必须等待被显式唤醒,否则将处于无限期的等待状态。2.6.超时等待(TIMED_WAITING)处于这种状态的线程不会被分配CPU执行时间,但它们不需要无限期地等待被其他线程唤醒,一定时间后会自动唤醒。2.7.终止状态(TERMINATED)当线程的run()方法完成时,或者当主线程的main()方法完成时,我们认为它已终止。这个线程对象可能还活着,但它不再是一个单独的执行线程。线程一旦终止,就无法复活。在终止的线程上调用start()方法将抛出java.lang.IllegalThreadStateException。3、等待队列在调用obj的wait()和notify()方法之前,必须先获得obj锁,即必须写在synchronized(obj)代码段中。与等待队列相关的步骤和图表线程1获取正在使用的对象A的锁。线程1调用了对象A的wait()方法,线程1释放了对象A的锁,立即进入等待队列。锁池中的对象竞争对象A的锁,线程5获得对象A的锁,进入synchronized块,使用对象A。线程5调用对象A的notifyAll()方法唤醒所有线程,所有线程进入同步队列。如果线程5调用对象A的notify()方法,一个线程被唤醒,谁被唤醒是未知的,被唤醒的线程进入同步队列。notifyAll()方法所在的synchronized结束,线程5释放了对象A的锁,同步队列的线程都在争夺对象锁,但是不知道线程1什么时候能抢到。4.同步队列状态当当前线程要调用对象A的同步方法时,发现对象A的锁被其他线程占用了。此时,当前线程进入同步队列。简而言之,同步队列中充满了想要竞争对象锁的线程。当一个线程1被另一个线程2唤醒时,线程1进入同步队列竞争对象锁。同步队列只是同步环境下的一个概念,一个对象对应一个同步队列。当等待时间到了或者线程被notify/notifyAll唤醒后,就会进入同步队列去竞争锁。如果获取到锁,则进入RUNNABLE状态,否则进入BLOCKED状态,等待获取锁。5、几种方法的比较Thread.sleep(longmillis)必须被当前线程调用。当前线程进入TIMED_WAITING状态,但没有释放对象锁。毫秒后,线程自动苏醒,进入就绪状态。作用:给其他线程一个执行机会的最佳方式。Thread.yield()必须由当前线程调用。当前线程放弃获得的CPU时间片,但不释放锁资源,从运行状态变为就绪状态,让OS重新选择线程。作用:让相同优先级的线程依次执行,但不保证一定会依次执行。实际上,并不能保证yield()一定会达到让出的目的,因为被让出的线程可能会被线程调度器再次选中。Thread.yield()不会导致阻塞。此方法类似于sleep(),只是用户无法指定暂停多长时间。thread.join()/thread.join(longmillis),当前线程调用其他线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者毫秒时间到后,当前线程一般进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。Object.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依赖notify()/notifyAll()唤醒或wait(longtimeout)超时自动唤醒。Object.notify()唤醒在此对象监视器上等待的单个线程,选择是任意的。notifyAll()唤醒所有等待这个对象监视器的线程。LockSupport.park()/LockSupport.parkNanos(longnanos),LockSupport.parkUntil(longdeadlines),当前线程进入WAITING/TIMED_WAITING状态。与wait方法相比,线程可以在不获取锁的情况下进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread线程)唤醒。创建线程的六种方式Thread线程的运行过程会产生很多信息,这些信息都保存在Thread类的成员变量中。常见的有:线程的ID是唯一标识符getId()b。线程名:getName(),如果没有设置线程名,默认为"Thread-xx"c.线程优先级:getPriority,线程优先级取值范围为1-10,其中数字越大,优先级越高,同时得到JVM调度执行的可能性越大,JDK内置三种常见状态:`//最低优先级publicfinalstaticintMIN_PRIORITY=1;//一般优先级publicfinalstaticintNORM_PRIORITY=5;//最高优先级publicfinalstaticintMAX_PRIORITY=10;`一般不建议设置线程的优先级。如果设置了非法优先级,则会发生IllegalArgumentException。创建线程的一种方式:继承Thread类。步骤:1.定义一个继承自Thread类的类。2.重写Thread类中的run方法。3、直接创建Thread的子类对象来创建线程。4.调用start方法启动线程,调用线程的taskrun方法执行。线程的名字可以通过Thread的getName获取。Thread-number的名字(从0开始)就是主线程的名字。classDemoextendsThread{/***线程名称*/privateStringname;Demo(Stringname){//父类构造器,改变线程名super(name);//this.name=名字;}//***run方法中定义的是线程要运行的任务代码。***publicvoidrun(){for(intx=0;x<10;x++){//for(inty=-9999999;y<999999999;y++){}System.out.println(name+"....x="+x+"......name="+Thread.currentThread().getName());}}}classThreadDemo2{publicstaticvoidmain(String[]args){Demod1=newDemo("旺财");Demod2=newDemo("小强");d1.start();//启动线程,调用run方法。d2.开始();System.out.println("结束...."+Thread.currentThread().getName());}}线程创建方法2当类有自己的父类时,通过实现Runnable接口,重写run方法。(通用)步骤:1.定义实现Runnable接口的类。2、重写接口中的run方法,将线程的任务代码封装到run方法中。3.通过Thread类创建线程对象,将Runnable接口的子类对象作为Thread类构造函数的参数传递。为什么?因为线程的任务都封装在Runnable接口的子类对象的run方法中。因此,必须在创建线程对象时指定要运行的任务。思路:通过Runnable接口将线程的任务封装成一个对象。4.调用线程对象的start方法启动线程。实现Runnable接口的好处:1、线程的任务从线程的子类中分离出来,进行单独的封装,按照面向对象的思想将任务封装成一个对象。2、避免java单继承的局限性。//extendsFu//准备扩展Demo类的功能,这样内容就可以作为线程任务来执行了。//通过接口的形式完成。类Demo实现Runnable{publicvoidrun(){show();}publicvoidshow(){for(intx=0;x<20;x++){System.out.println(Thread.currentThread().getName()+"...."+x);}}}classThreadDemo{publicstaticvoidmain(String[]args){Demod=newDemo();线程t1=新线程(d);线程t2=新线程(d);t1.开始();t2.开始();}}线程创建方法3实现Callable接口与使用Runnable相比,Callable更强大1与run()方法相比,它可以有返回值2该方法可以抛出异常3支持泛型返回值4需要使用FutureTask类,如获取返回结果Future接口1可以取消具体Runnable和Callable任务的执行结果,查询是否完成,获取结果等2FutureTask是Future接口的唯一实现类3FutureTask同时实现了Runnable和Future接口。可以作为Runnable被线程执行,也可以作为Future获取Callable的返回值//1。创建一个实现Callableclass的实现类StuimplementsCallable{//2.实现call方法使本线程需要执行的操作生效@OverridepublicObjectcall()incall()throwsException{intsum=0;for(inti=1;i<=100;i++){if(i%2==0){System.out.println(i);总和+=我;}}返回总和;}}publicclassBank{publicstaticvoidmain(String[]args){//3.创建一个Callable接口实现类的对象Stustu=newStu();//4.将Callable接口实现类的对象传递给FutureTask构造函数,创建FutureTask对象FutureTaskfutureTask=newFutureTask(stu);//5。将FutureTask对象作为参数传递给Thread类的构造函数创建Thread,并调用start()newThread(futureTask).start();尝试{对象总和=futureTask.get();System.out.println("求和为"+sum);}catch(InterruptedExceptione){e.printStackTrace();}catch(ExecutionExceptione){e.printStackTrace();}}}线程创建方法4使用线程池背景:经常创建和销毁的资源,使用大量资源,比如并发情况下的线程,对性能影响很大思路:提前创建多个线程,把它们在线程池中,使用时直接获取,用完再放回池中。可以避免频繁的创建和销毁,实现复用。类似于生活中的公共交通。好处:1提高响应速度(减少创建新线程的时间);2减少资源消耗(复用线程池中的线程,不需要每次都创建);3方便线程管理;corePoolSize:核心池的大小maximumPoolSize:最大线程数keepAliveTime:当线程没有任务时,最多持续多久后终止;1;我<=100;i++){if(i%2==0){System.out.println(Thread.currentThread().getName()+":"+i);}}}}publicclassThreadPool{publicstaticvoidmain(String[]args){//1.提供指定线程数ExecutorServiceservice=Executors.newFixedThreadPool(10);//设置线程属性ThreadPoolExecutorservice1=(ThreadPoolExecutor)service;//service1.setMaximumPoolSize(15);//service1.setCorePoolSize();*///2.将Runnable实现类的对象作为形参传递给ExecutorService的submit()方法,启动线程//并执行相关的run()服务。execute(newMyThread());//适用于Runnable//service.submit();适用于Callable//3.结束线程使用service.shutdown();}}