java 정시 실현 방법 및 실례 코드
우리가 프로그래밍하는 과정에서 만약에 간단한 정시 임무를 수행해야 한다면 복잡한 제어를 할 필요가 없다. 우리는 JDK의 Timer 정시 임무를 사용하여 실현하는 것을 고려할 수 있다.다음 LZ는 그 원리, 실례, 그리고 Timer 결함 세 가지 측면에서 자바 타이머 타이머를 분석한다.
1. 소개
Java에서 전체 타이밍 작업은 Timer, TimerTask 두 클래스로 이루어져야 합니다.API에서 이들을 정의합니다. Timer: 라인은 백그라운드 라인에서 실행할 작업을 배정하는 도구입니다.임무를 한 번 수행하거나 정기적으로 반복할 수 있습니다.TimerTask: Timer에서 한 번에 수행하거나 반복할 작업으로 스케줄링합니다.우리는 Timer가 일종의 타이머 도구라는 것을 이해할 수 있다. 이것은 백엔드 라인에서 지정된 임무를 수행하는 데 사용되고, TimerTask는 추상적인 클래스로 그 하위 클래스는 Timer가 계획할 수 있는 임무를 대표한다.
Timer 클래스
도구 클래스 Timer에서 네 가지 구조 방법을 제공했다. 모든 구조 방법은 타이머 라인을 가동했다. 이 동시에 Timer 클래스는 여러 개의 라인이 하나의 Timer 대상을 공유할 수 있고 외부 동기화를 하지 않아도 되기 때문에 Timer 클래스는 라인이 안전하다.그러나 모든 Timer 대상이 대응하는 것은 하나의 백그라운드 라인이기 때문에 모든 타이머 임무를 순서대로 수행하는 데 사용된다. 일반적인 상황에서 우리의 라인 임무 수행에 소모되는 시간은 매우 짧아야 한다. 그러나 특수한 상황으로 인해 특정한 타이머 임무 수행 시간이 너무 길면 타이머의 임무 수행 라인을 독점하고 그 후의 모든 라인은 반드시 그것이 실행되기를 기다려야 한다.이것은 후속 임무의 집행을 지연시켜 이 임무들을 한데 쌓게 할 것이다. 구체적인 상황은 우리가 뒤에서 분석한다.
프로그램이 초기화되어 Timer를 완성하면 정해진 시간에 작업이 실행됩니다. Timer는 schedule 방법을 제공합니다. 이 방법은 다중 재부팅 방식으로 서로 다른 상황에 적응할 수 있습니다. 다음과 같습니다.
schedule(TimerTask task, Date time): 지정된 시간에 지정된 작업을 수행하도록 설정합니다.
schedule(TimerTask task, Date firstTime, long period): 지정한 작업을 지정된 시간에 반복적인 고정 지연 실행을 시작합니다.
schedule(TimerTask task, long delay): 지정된 지연 시간 후에 지정된 작업을 수행하도록 설정합니다.
schedule(TimerTask task,long delay,long period): 지정한 작업을 지정한 지연부터 반복적인 고정 지연으로 실행합니다.
또한 schedule AtFixedRate 방법을 다시 불러왔다. schedule AtFixedRate 방법은schedule와 같지만 그들의 측면 중점이 다르고 뒷부분의 분석을 구별한다.
schedule AtFixedRate(TimerTask task, Date firstTime, long period): 지정된 시간부터 반복적인 고정 속도로 작업을 수행합니다.
schedule AtFixedRate(TimerTask task,long delay,long period): 지정한 작업을 지정한 지연 시간 후에 반복적인 고정 속도로 실행합니다.
TimerTask
TimerTask 클래스는 Timer가 한 번에 수행하거나 반복적으로 수행하도록 설정한 추상적인 클래스입니다.이것은 추상적인 방법인 run () 방법을 가지고 있으며, 이 방법은 해당하는 타이머 임무를 수행하는 데 사용된다.따라서 모든 구체적인 작업 클래스는 TimerTask를 계승하고 run() 방법을 다시 써야 한다.
그리고 그것은 두 가지 비추상적인 방법이 있다.
boolean cancel (): 이 타이머 작업을 취소합니다.
long scheduledExecutionTime (): 이 작업의 최근 실제 실행 스케줄을 되돌려줍니다.
2. 실례
2.1, 주어진 시간 지연 작업 수행
public class TimerTest01 {
Timer timer;
public TimerTest01(int time){
timer = new Timer();
timer.schedule(new TimerTaskTest01(), time * 1000);
}
public static void main(String[] args) {
System.out.println("timer begin....");
new TimerTest01(3);
}
}
public class TimerTaskTest01 extends TimerTask{
public void run() {
System.out.println("Time's up!!!!");
}
}
실행 결과:
:timer begin....
3 :Time's up!!!!
2.2 지정된 시간에 작업을 수행합니다.
public class TimerTest02 {
Timer timer;
public TimerTest02(){
Date time = getTime();
System.out.println(" time=" + time);
timer = new Timer();
timer.schedule(new TimerTaskTest02(), time);
}
public Date getTime(){
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 11);
calendar.set(Calendar.MINUTE, 39);
calendar.set(Calendar.SECOND, 00);
Date time = calendar.getTime();
return time;
}
public static void main(String[] args) {
new TimerTest02();
}
}
public class TimerTaskTest02 extends TimerTask{
@Override
public void run() {
System.out.println(" ...");
}
}
시간이 11:39:00에 도달하면 이 라인 임무를 수행합니다. 물론 이 시간보다 크면 수행합니다!!실행 결과:
time=Tue Jun 10 11:39:00 CST 2014
...
2.3 지정된 시간을 지체한 후 지정된 간격으로 순환하여 시간 작업을 수행합니다.
public class TimerTest03 {
Timer timer;
public TimerTest03(){
timer = new Timer();
timer.schedule(new TimerTaskTest03(), 1000, 2000);
}
public static void main(String[] args) {
new TimerTest03();
}
}
public class TimerTaskTest03 extends TimerTask{
@Override
public void run() {
Date date = new Date(this.scheduledExecutionTime());
System.out.println(" :" + date);
}
}
실행 결과:
:Tue Jun 10 21:19:47 CST 2014
:Tue Jun 10 21:19:49 CST 2014
:Tue Jun 10 21:19:51 CST 2014
:Tue Jun 10 21:19:53 CST 2014
:Tue Jun 10 21:19:55 CST 2014
:Tue Jun 10 21:19:57 CST 2014
.................
이 스레드 임무에 대해 만약 우리가 이 임무를 멈추지 않는다면, 그는 계속 운행할 것이다.위의 세 가지 실례에 대해 LZ는 간단하게 설명했을 뿐 schedule AtFixedRate 방법을 설명하는 예도 없다. 사실 이 방법은 schedule 방법과 같다!
2.4, schedule 및 scheduleAtFixedRate 분석
1、schedule(TimerTask task, Date time)、schedule(TimerTask task, long delay)
이 두 가지 방법에 대해 말하자면, 지정된 계획 실행 시간 scheduledExecution Time<= systemCurrentTime,task가 즉시 실행됩니다.scheduledExecution Time은 어떤 task의 과도한 실행으로 인해 바뀌지 않습니다.
2、schedule(TimerTask task, Date firstTime, long period)、schedule(TimerTask task, long delay, long period)
이 두 가지 방법은 위의 두 가지와 약간 다르다. 앞에서 언급한 타이머의 타이머 작업은 이전 작업의 수행 시간이 비교적 길어서 지연될 수 있다.이 두 가지 방법 중 매번 실행되는task의 계획 시간은 이전task의 실제 시간에 따라 바뀐다. 즉, scheduledExecutionTime(n+1)=realExecutionTime(n)+periodTime이다.즉, 만약에 n번째 task가 어떤 상황으로 인해 이번 실행 시간 과정을 초래하면 마지막에 시스템CurrentTime>=scheduledExecutionTime(n+1)을 초래한다. 이것은 n+1번째 task가 때가 되었다고 실행하지 않는다. 그는 n번째 task가 실행된 후에 실행하기를 기다린다.그러면 n+2개의 실행이 scheduled Execution Time의 방출 변화를 초래할 것이다. 즉, scheduled Execution Time(n+2) = Real Execution Time(n+1)+period Time이다.그래서 이 두 가지 방법은 보존 간격의 안정을 더욱 중시한다.
3、scheduleAtFixedRate(TimerTask task, Date firstTime, long period)、scheduleAtFixedRate(TimerTask task, long delay, long period)
앞에서 언급한 바와 같이 schedule AtFixedRate와 schedule 방법의 측면 중점은 다르다. schedule 방법은 저장 간격의 안정에 중심을 두고 schedule AtFixedRate 방법은 실행 주파수의 안정을 유지하는 데 더욱 중심을 둔다.왜 이렇게 말하는가, 원인은 다음과 같다.schedule 방법에서는 이전 작업의 지연으로 인해 그 뒤의 정시 작업이 지연될 수 있지만, scheduleAtFixedRate 방법은 그렇지 않습니다. 만약 n번째 task 실행 시간이 너무 길어서 systemCurrentTime>=scheduledExecutionTime(n+1)을 초래하면 그가 즉시 n+1 task를 실행할 때까지 기다리지 않기 때문에 scheduleatFixedRate 방법의 실행 시간 계산 방법은 schedule와 같지 않습니다.scheduledExecutionTime(n)=firstExecuteTime+n*periodTime입니다. 이 계산 방법은 영원히 변하지 않습니다.그래서 schedule AtFixedRate는 실행 주파수의 안정을 유지하는 데 더욱 중심을 두었다.
3. Timer의 결함
3.1. Timer의 결함
Timer 타이머는 시간 (지정된 시간에 작업을 수행), 지연 (5초 지연), 주기적으로 작업 (1초 간격으로 작업을 수행) 을 수행할 수 있지만, Timer에 결함이 있습니다.우선 Timer의 스케줄링에 대한 지원은 상대적인 시간이 아니라 절대적인 시간에 기초하기 때문에 시스템 시간의 변화에 매우 민감하다.그 다음으로 Timer 스레드는 이상을 포착하지 않습니다. 만약에 Timer Task가 검사를 하지 않은 이상을 던지면 Timer 스레드가 종료되고 Timer 스레드의 실행을 다시 회복하지 않습니다. 그는 전체 Timer 스레드가 취소될 것이라고 잘못 생각할 것입니다.또한 이미 스케줄러가 실행되지 않은 TimerTask도 더 이상 실행되지 않고 새로운 임무도 스케줄링될 수 없습니다.따라서 TimerTask가 검사하지 않은 이상을 던지면 Timer는 예측할 수 없는 행동을 하게 됩니다.
1. Timer 관리 시간 지연 결함
앞의 Timer는 타이밍 작업을 수행할 때 하나의 스레드 작업만 생성합니다. 만약 여러 개의 스레드가 존재한다면, 그 중 어떤 스레드가 어떤 원인으로 인해 스레드 작업의 수행 시간이 너무 길어지고, 두 작업의 간격을 초과하면 일부 결함이 발생할 수 있습니다.
public class TimerTest04 {
private Timer timer;
public long start;
public TimerTest04(){
this.timer = new Timer();
start = System.currentTimeMillis();
}
public void timerOne(){
timer.schedule(new TimerTask() {
public void run() {
System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));
try {
Thread.sleep(4000); // 3000
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 1000);
}
public void timerTwo(){
timer.schedule(new TimerTask() {
public void run() {
System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));
}
}, 3000);
}
public static void main(String[] args) throws Exception {
TimerTest04 test = new TimerTest04();
test.timerOne();
test.timerTwo();
}
}
우리의 정상적인 사고방식에 따르면 timerTwo는 3s 이후에 실행되어야 한다. 그 결과는 다음과 같다.
timerOne invoked ,the time:1001
timerOne invoked ,the time:3001
그러나 뜻대로 되지 않았다. timerOne은 sleep(4000)으로 4S를 휴면했고 Timer 내부가 하나의 라인으로 되어 있어 timeOne에 필요한 시간이 간격을 초과했다. 결과:
timerOne invoked ,the time:1000
timerOne invoked ,the time:5000
2、Timer가 이상한 결함을 던진다TimerTask에서 RuntimeException을 내보내면 Timer는 모든 작업을 종료합니다.다음과 같습니다.
public class TimerTest04 {
private Timer timer;
public TimerTest04(){
this.timer = new Timer();
}
public void timerOne(){
timer.schedule(new TimerTask() {
public void run() {
throw new RuntimeException();
}
}, 1000);
}
public void timerTwo(){
timer.schedule(new TimerTask() {
public void run() {
System.out.println(" ??");
}
}, 1000);
}
public static void main(String[] args) {
TimerTest04 test = new TimerTest04();
test.timerOne();
test.timerTwo();
}
}
실행 결과:timerOne에서 이상을 던져 timerTwo 작업이 종료되었습니다.
Exception in thread "Timer-0" java.lang.RuntimeException
at com.chenssy.timer.TimerTest04$1.run(TimerTest04.java:25)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Timer의 결함에 대해 우리는 ScheduledThreadPoolExecutor를 고려하여 대체할 수 있다.Timer는 절대 시간을 바탕으로 하고 시스템 시간에 민감하며 ScheduledThreadPoolExecutor는 상대 시간을 바탕으로 한다.Timer는 내부는 단일 라인이고 ScheduledThreadPoolExecutor 내부는 하나의 라인 탱크이기 때문에 여러 개의 작업을 동시에 수행할 수 있습니다.3.2 Timer 대신 ScheduledExecutorService 사용
1. 문제 해결1:
public class ScheduledExecutorTest {
private ScheduledExecutorService scheduExec;
public long start;
ScheduledExecutorTest(){
this.scheduExec = Executors.newScheduledThreadPool(2);
this.start = System.currentTimeMillis();
}
public void timerOne(){
scheduExec.schedule(new Runnable() {
public void run() {
System.out.println("timerOne,the time:" + (System.currentTimeMillis() - start));
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},1000,TimeUnit.MILLISECONDS);
}
public void timerTwo(){
scheduExec.schedule(new Runnable() {
public void run() {
System.out.println("timerTwo,the time:" + (System.currentTimeMillis() - start));
}
},2000,TimeUnit.MILLISECONDS);
}
public static void main(String[] args) {
ScheduledExecutorTest test = new ScheduledExecutorTest();
test.timerOne();
test.timerTwo();
}
}
실행 결과:
timerOne,the time:1003
timerTwo,the time:2005
2. 문제 해결 2
public class ScheduledExecutorTest {
private ScheduledExecutorService scheduExec;
public long start;
ScheduledExecutorTest(){
this.scheduExec = Executors.newScheduledThreadPool(2);
this.start = System.currentTimeMillis();
}
public void timerOne(){
scheduExec.schedule(new Runnable() {
public void run() {
throw new RuntimeException();
}
},1000,TimeUnit.MILLISECONDS);
}
public void timerTwo(){
scheduExec.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("timerTwo invoked .....");
}
},2000,500,TimeUnit.MILLISECONDS);
}
public static void main(String[] args) {
ScheduledExecutorTest test = new ScheduledExecutorTest();
test.timerOne();
test.timerTwo();
}
}
실행 결과:
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
........................
읽어주셔서 감사합니다. 여러분에게 도움이 되었으면 좋겠습니다. 본 사이트에 대한 지지에 감사드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
38. Java의 Leetcode 솔루션텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.