11、下面的代码,实际上有几个线程在运行:
两个:线程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,那它也就无法同步了。作用于线程
实现原理
yield(): 它仅仅释放线程所占有的CPU资源,从而让其他线程有机会运行,但是并不能保证某个特定的线程能够获得CPU资源。谁能获得CPU完全取决于调度器,在有些情况下调用yield方法的线程甚至会再次得到CPU资源。所以,依赖于yield方法是不可靠的,它只能尽力而为。作用于线程
wait():
· wait只能在同步(synchronize)环境中被调用,而sleep不需要。
· 进入wait状态的线程能够被notify和notifyAll线程唤醒,但是进入sleeping状态的线程不能被notify方法唤醒。
· wait通常有条件地执行,线程会一直处于wait状态,直到某个条件变为真。但是sleep仅仅让你的线程进入睡眠状态。
· wait方法在进入wait状态的时候会释放对象的锁,但是sleep方法不会。
wait方法是针对一个被同步代码块加锁的对象
见代码ThreadBlockTest1-5
14、为什么不推荐使用stop和destroy方法来结束线程的运行?
stop():此方法可以强行中止一个正在运行或挂起的线程。但stop方法不安全,就像强行切断计算机电源,而不是按正常程序关机。可能会产生不可预料的结果。
举例来说:
当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,
假如一个线程正在执行:
由于方法是同步的,多个线程访问时总能保证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一般是以匿名类的方式创建。
语法简介:
用法简介:
//以下是几种调度task的方法:
// time为Date类型:在指定时间执行一次。
// firstTime为Date类型,period为long
// 从firstTime时刻开始,每隔period毫秒执行一次。
// delay 为long类型:从现在起过delay毫秒执行一次
// delay为long,period为long:从现在起过delay毫秒以后,每隔period
// 毫秒执行一次。
举例:
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() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择。