1. Java中sleep和wait的区别
① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
② 锁: 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。
Thread.sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
③ 使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。
synchronized(x){
x.notify()
//或者wait()
}
2.可能在面试的时候面试官会问你java实现多线程的方式有哪些,你可能会知道java要提供继承Thread类和实现runnable接口这两种方式来实现线程,但是如果问为什么这样设计呢?
首先说一下,我们知道java的集成只能是单继承,不能多集成,这样的话就会有缺陷,比如想增加一个功能的时候必须要去修改基类。而实现runnable接口的这种方式可以很好的解决java不能多继承导致的缺陷。这是第一个原因。
再说第二个,我们知道实现runnable接口的方式代码的写法是这样的:new Thread(new runnable(){ public void run(){ ....}})。在这种情况下我们可以看到其实整个的runnable对象中的代码可以被多个Thread对象实例所使用共享,这样就可以解决一个多个线程处理同一资源的情况。做到了线程安全。在这里我觉着有必要通过一个代码的方式来解释一下第二个优点是如何实现的。
3.首先来说一下Timer是怎么工作的,Timer 是按照一定的时间段或者一个时间点根据定时的定时任务进行执行的。
Timer这个java提供的定时器有如下的特点:
① 他是一个单线程的,也就是你启动一个Timer定时器就是启动了一个线程。
② Timer定时器默认的情况下不是守护线程,但是可以通过构造参数设置为守护线程,守护线程在没有其他线程的情况下自己会挂掉。
③ 使用Timer定时器的时候 要跟一个TimerTask定时任务结合来使用。而且TimerTask其实底层就是一个队列,在TimerTask中增加的任务会在定时器这个线程里面挨个的执行。TimerTask也有自己的cannel取消等方法。
④ TimerTask中的run方法无法抛出,所以要进行try catch捕获,如果其中任何一个任务发生异常没有被捕获,则其他任务也将被终止。
我们看到TImer定时器这个类有两个schedule方法。其中都有的就是一个TimerTask这个任务。我对这两个方法进行了一个总结:
方法详解:
(1)schedule(TimerTask task, Date executeTime)
当executeTime<=currentTime时,task任务会在currentTimer立即执行
当executeTime>currentTime时,task会在未来的executeTime执行
(2)schedule(TimerTask task, Date firstTime, long period)
当firstTime <=currentTime时,task任务会在currentTimer立即执行,
当firstTime >currentTime时,task会在未来的executeTime执行,
执行任务所用的时间taskUsedTime<peroid,则下一个任务执行的时间是上次任务执行完成的时间+peroid,任务按时间间隔peroid周期性执行任务
执行任务所用的时间taskUsedTime>peroid,则下一个任务执行的时间是上次任务执行完成的时间+taskUsedTime,任务按时间间隔taskUsedTime 周期性执行任务
(3)schedule(TimerTask task, long delay)
任务延迟delay毫秒进行执行
(4)schedule(TimerTask task, long delay, long period)
A、延迟delay毫秒第一次执行,
B、执行任务所用的时间taskUsedTime<peroid,则下一个任务执行的时间是上次任务执行完成的时间+peroid, 任务按时间间隔peroid周期性执行任务
C、执行任务所用的时间taskUsedTime>peroid,则下一个任务执行的时间是上次任务执行完成的时间+taskUsedTime, 任务按时间间隔taskUsedTime 周期性执行任务
(5)scheduleAtFixedRate(TimerTask task, long delay, long period)
(6)scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
startTime = currentTime
A、当firstTime>currentTime,任务则在currentTime执行
B、当firstTime<currentTime,任务会发生追赶执行,追赶执行的次数expectCount=(currentTime-firstTime)/peroid+1;
第一个peroid属于追赶阶段,如果追赶上则等待执行startTime+peroid时间任务,如果没有追赶上则直接执行startTime+peroid时间的任务
首先我们看一下Timer这个类的构造函数,因为我们知道我们再使用Timer这个类的时候我们只是创建了一个Timer对象,并没有像Thread那样主动的去调用start方法。所以我想答应也应该明白我们定时器的启动是在构造函数中做的,没错,从源码中我们可以得道验证:
Timer是一个单独的线程,从第152行我们就可以看到,我们可以设置线程的名称,可以设置是否是守护线程,然后调用start方法定时器就起作用了。但是并不会立即执行。线程调用start后也不会立即执行,这在上一篇中已经有说到了,他其实是把当前的线程示例放到了一个线程组中等待被执行。
哪我们就看他是如何调度的也就是schdule是如何执行的呢。
这里我们补充一下,queue是一个TaskQueue