Java面试老失利?一定是多线程面试答案有问题!

11、下面的代码,实际上有几个线程在运行:

  

cfedc1a56ecb4ffeb5f7df0708bee65e.png

两个:线程t和main()方法(主线程)。

12、线程的几种状态

1.线程通常有五种状态,创建,就绪,运行、阻塞和死亡状态。

2.阻塞的情况又分为三种:

  • (1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,wait是object类的方法

  • (2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。

  • (3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。sleep是Thread类的方法

  • 1.新建状态(New):新创建了一个线程对象。

  • 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

  • 3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  • 4.阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

  • 5.死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

13、说说:sleep、yield、join、wait方法的区别。

sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是sleep()方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。 作用于线程

  • ·  Thread.sleep()方法用来暂停线程的执行,将CPU放给线程调度器。

  • ·  Thread.sleep()方法是一个静态方法,它暂停的是当前执行的线程。

  • ·  Java有两种sleep方法,一个只有一个毫秒参数,另一个有毫秒和纳秒两个参数。

  • ·  与wait方法不同,sleep方法不会释放锁

  • ·  如果其他的线程中断了一个休眠的线程,sleep方法会抛出Interrupted Exception。

  • ·  休眠的线程在唤醒之后不保证能获取到CPU,它会先进入就绪态,与其他线程竞争CPU。

  • ·  有一个易错的地方,当调用t.sleep()的时候,会暂停线程t。这是不对的,因为Thread.sleep是一个静态方法,它会使当前线程而不是线程t进入休眠状态。


join(): 当前线程等待调用此方法的线程执行结束再继续执行。如:在main方法中调用t.join(),那main方法在此时进入阻塞状态,一直等t线程执行完,main方法再恢复到就绪状态,准备继续执行。

join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。作用于线程

实现原理

0d19868471f745498f84cb58e4e09629.png

yield(): 它仅仅释放线程所占有的CPU资源,从而让其他线程有机会运行,但是并不能保证某个特定的线程能够获得CPU资源。谁能获得CPU完全取决于调度器,在有些情况下调用yield方法的线程甚至会再次得到CPU资源。所以,依赖于yield方法是不可靠的,它只能尽力而为。作用于线程

wait():

·       wait只能在同步(synchronize)环境中被调用,而sleep不需要。

·       进入wait状态的线程能够被notify和notifyAll线程唤醒,但是进入sleeping状态的线程不能被notify方法唤醒。

·       wait通常有条件地执行,线程会一直处于wait状态,直到某个条件变为真。但是sleep仅仅让你的线程进入睡眠状态。

·       wait方法在进入wait状态的时候会释放对象的锁,但是sleep方法不会。

wait方法是针对一个被同步代码块加锁的对象

8661a29c0b4a48faa98fa3b2f4707bb6.png

见代码ThreadBlockTest1-5

14、为什么不推荐使用stop和destroy方法来结束线程的运行?

stop():此方法可以强行中止一个正在运行或挂起的线程。但stop方法不安全,就像强行切断计算机电源,而不是按正常程序关机。可能会产生不可预料的结果。

举例来说:

当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,

假如一个线程正在执行:

8438051010144d44adfdc80310dd87e3.png

由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记 线程的stop方法,以后我们再也不要说“停止线程”了。

destroy():该方法最初用于破坏该线程,但不作任何资源释放。它所保持的任何监视器都会保持锁定状态。不过,该方法决不会被实现。即使要实现,它也极有可能以 suspend() 方式被死锁。如果目标线程被破坏时保持一个保护关键系统资源的锁,则任何线程在任何时候都无法再次访问该资源。如果另一个线程曾试图锁定该资源,则会出现死锁。

15、写个代码说明,终止线程的典型方式。

(1)    当run()方法执行完后,线程就自动终止了。

(2)    但有些时候run()方法不会结束(如服务器端监听程序),或者其它需要用循环来处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){……}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。

见代码ThreadEndTest

代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值。

16、A线程的优先级是10,B线程的优先级是1,那么当进行调度时一定会调用A吗?

不一定。线程优先级对于不同的线程调度器可能有不同的含义,可能并不是用户直观的推测。

见代码ThreadPriorityTest

17、synchronize修饰在方法前是什么意思?

一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.

见代码SynchronizedTest1

18、使用Timer和TimerTask实现定时执行,定时在每天下午17:00执行。

见代码TimerTest1-3

知识点简介:

(1)        Timer:定时器,实际上是个线程,定时调度所拥有的TimerTasks。

(2)        TimerTask:一个拥有run方法的类,需要定时执行的代码放到run方法体内。 TimerTask一般是以匿名类的方式创建。

语法简介:

4060724e0a29480ea773cf8478541df7.png

用法简介:

//以下是几种调度task的方法:  

21952e04f3384ee4b8a682e8f958eb8b.png

// time为Date类型:在指定时间执行一次。  

6c4d910f8e024c3fbf43c0005d75df86.png

// firstTime为Date类型,period为long  

// 从firstTime时刻开始,每隔period毫秒执行一次。  

c3ad8c72283144f89626fc5dc5b97cc8.png

// delay 为long类型:从现在起过delay毫秒执行一次  

06d512246fa34a31af3cbbd548c2f71b.png

// delay为long,period为long:从现在起过delay毫秒以后,每隔period  

// 毫秒执行一次。 

   举例:

18cf602858bd45f4aec996e8d277f0f3.png

19、wait方法被调用时,所在线程是否会释放所持有的锁资源? sleep方法呢?

wait:释放CPU,释放锁;

sleep:释放CPU,不释放锁。

20、wait、notify、notifyAll是在Thread类中定义的方法吗?作用分别是什么?

wait(),notify(),notifyAll()不属于Thread类,而是属于Object类,也就是说每个对象都有wait(),notify(),notifyAll()的功能。

因为每个对像都有锁,锁是每个对像的基础,而wait(),notify(),notifyAll()都是跟锁有关的方法。

三个方法的作用分别是:

  • wait:导致当前线程等待,进入阻塞状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。当前线程必须拥有此对象监视器(对象锁)。该线程释放对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行.

  • notify:唤醒在此对象监视器(对象锁)上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。此方法只应由作为此对象监视器的所有者的线程来调用.

    "当前线程必须拥有此对象监视器"与"此方法只应由作为此对象监视器的所有者的线程来调用"说明wait方法与    notify方法必须在同步块内执行,即synchronized(obj之内).

  • notifyAll: 唤醒在此对象监视器(对象锁)上等待的所有线程。

21、notify是唤醒所在对象wait pool中的第一个线程吗?

不是。

调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择。

22ceb816a8f04a6a97784f9451cd0262.png

猜你喜欢

转载自blog.csdn.net/Assdqd/article/details/86538585
今日推荐