कई प्रक्रियाओं में `System.currentTimeMillis ()` सही है?
हमारे पास एक ऐसी स्थिति है जहां एक मास्टर प्रक्रिया लॉग में लिखती है।
यह तब कई कार्यकर्ता प्रक्रियाओं को जन्म देता है जो अपने लॉग में लिखते हैं। (मैं चाहता था कि कार्यकर्ता मास्टर के माध्यम से लॉग इन करें, लेकिन किसी कारण से इस विचार का विरोध था।)
मैं जो जानना चाहता हूं, क्या मैं भरोसा कर सकता हूं कि कई फाइलों में समाप्त होने वाली टाइमस्टैम्प एक-दूसरे के अनुरूप हैं? यानी, अगर मैं लॉग फाइल को एक ही फाइल में इंस्टेंट करके सॉर्ट करता हूं, तो क्या घटनाओं का क्रम सही होगा? सभी संभव ऑपरेटिंग सिस्टम के पार?
इसका कारण मैं यह पूछ रहा हूं कि मेरे पास एक अजीब स्थिति है जहां ऐसा लगता है जैसे कार्यकर्ता ने दो सेकंड में त्रुटि दर्ज की है क्योंकि मास्टर ने रिपोर्ट किया है कि कार्यकर्ता को एक त्रुटि हुई थी। यह ऐसा है जैसे गुरु भविष्य में देख सकता था। (मुझे लगता है कि स्वामी भी एक समय के स्वामी हैं, लेकिन उह ...)
जवाब
होस्ट OS और अंतर्निहित कंप्यूटर क्लॉक हार्डवेयर द्वारा रिपोर्ट किए गए वर्तमान क्षण को कॉल System.currentTimeMillisऔर उसके आधुनिक प्रतिस्थापन Instant.nowदोनों को कैप्चर करते हैं। Javadoc और स्रोत कोड एक घड़ी का वादा करते हैं "सर्वश्रेष्ठ उपलब्ध सिस्टम घड़ी के आधार पर"।
तो, नहीं, भविष्य में कूदना नहीं चाहिए । जब भी आप उन तरीकों में से किसी एक को कॉल करते हैं, तो आप वर्तमान क्षण को कैप्चर करते हैं।
हालाँकि, आप भविष्य में कूदने का भ्रम देख सकते हैं । इन कारणों से ऐसा हो सकता है:
- धागा निर्धारण
- घड़ी रीसेट करना
- नकली घड़ी
धागा निर्धारण
यह भ्रम हो सकता है क्योंकि वर्तमान क्षण के कैप्चर होने के बाद क्या होता है। वर्तमान क्षण को कैप्चर करने के बाद विभाजित दूसरा, उस थ्रेड के निष्पादन को रोका जा सकता है। कुछ अन्य सूत्र बाद में एक पल को कैप्चर कर सकते हैं, उस पल की रिपोर्ट करना जारी रख सकते हैं। आखिरकार, वह पहला धागा फिर से शुरू होता है, और उसके पहले कैप्चर किए गए पल की रिपोर्ट करता है - लेकिन ध्यान दें कि उस पल की रिपोर्टिंग बाद में कैसे होती है।
इस उदाहरण कोड को लें।
package work.basil.example;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TellTime
{
public static void main ( String[] args )
{
TellTime app = new TellTime();
app.demo();
}
private void demo ( )
{
ExecutorService executorService = Executors.newCachedThreadPool();
int countThreads = 15;
List < Callable < Object > > tasks = new ArrayList <>( countThreads );
for ( int i = 0 ; i < countThreads ; i++ )
{
Runnable tellTimeRunnable = ( ) -> System.out.println( Instant.now() );
tasks.add( Executors.callable( tellTimeRunnable ) );
}
try
{
List < Future < Object > > list = executorService.invokeAll( tasks );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
}
}
पहली बार जब मैंने उस कोड को चलाया, तो मुझे आउटपुट की अंतिम दो लाइनों में ऐसी छलांग मिली। चौथी पंक्ति 3 जी की तुलना में एक पल पहले दिखाई देती है। 5 वीं पंक्ति एक पल पहले भी दिखाती है।
2020-11-23T01:07:34.305318Z
2020-11-23T01:07:34.305569Z
2020-11-23T01:07:34.305770Z
2020-11-23T01:07:34.305746Z
2020-11-23T01:07:34.305434Z
यहां मेरे मामले में, System.out.printlnउनके निष्पादन में देरी से कॉल आने लगे , इसलिए कुछ पहले के क्षणों की सूचना बाद में दी गई। इसी तरह, मुझे संदेह है कि आपके मामले में, आपके कैप्चर किए गए क्षणों को लॉग करने की क्रिया में विभिन्न विलंब शामिल थे ताकि कुछ पहले के क्षणों को बाद में लॉग किया गया।
घड़ी रीसेट करना
जैसा कि स्टीफन सी नीचे टिप्पणियों में बताते हैं , कंप्यूटर अक्सर एक समय सर्वर से जानकारी के आधार पर हार्डवेयर घड़ी को ऑटो-समायोजित करने के लिए कॉन्फ़िगर किया जाता है। कई कंप्यूटरों में हार्डवेयर घड़ियां आपके द्वारा कल्पना की गई तुलना में कम सटीक हैं। तो होस्ट कंप्यूटर की घड़ी समय-समय पर बहाव को ठीक करने के लिए पहले या बाद के समय के लिए अच्छी तरह से रीसेट हो सकती है।
ज्ञात हो कि कुछ कंप्यूटर अपनी घड़ी को एक युगीन संदर्भ बिंदु पर वापस सेट कर लेते हैं जैसे 1970-01-01 00: 00Z जब हार्डवेयर घड़ी का दोषपूर्ण या समाप्त बैटरी / कैपेसिटर से बूट किया जाता है। उस युग संदर्भ समय को वर्तमान क्षण के रूप में रिपोर्ट किया जा सकता है जब तक कि कंप्यूटर को समय सर्वर के साथ जांचने का मौका नहीं मिलता है।
या कुछ मानव कंप्यूटर घड़ी की वर्तमान तिथि और समय को मैन्युअल रूप से समायोजित कर सकते हैं। :-(
आपका कोड इस घड़ी समायोजन के दोनों ओर वर्तमान क्षण को कैप्चर कर सकता है। अब एक बाद की घटना पहले हुई हो सकती है।
नकली घड़ी
में java.time , के रूप में इस तरह के कॉल Instant.nowवर्तमान-निर्दिष्ट एक्सेस Clockकार्यान्वयन। "वर्तमान में असाइन किया गया" द्वारा, मैं इस तथ्य का उल्लेख करता हूं कि java.time में , डिफ़ॉल्ट Clockऑब्जेक्ट को ओवरराइड किया जा सकता है। आमतौर पर यह केवल परीक्षण उद्देश्यों के लिए होगा। विभिन्न Clockऑब्जेक्ट एक निश्चित क्षण , एक स्थानांतरित क्षण , या परिवर्तित ताल के साथ रिपोर्ट कर सकते हैं ।
इस बात से अवगत रहें कि कोई वैकल्पिक Clockजानबूझकर एक अलग समय बता सकता है, यदि आपके परीक्षण कोड ने वैकल्पिक Clockऑब्जेक्ट निर्दिष्ट किया हो । डिफ़ॉल्ट रूप से यद्यपि आप हमेशा उस समय को वर्तमान समय प्राप्त करते हैं जब विधि कॉल निष्पादित होती है।
निष्कर्ष
यहाँ एक प्रमुख निहितार्थ है: समय-ट्रैकिंग पर पूरी तरह से भरोसा नहीं किया जा सकता है । वर्तमान क्षण को गलत तरीके से कैप्चर किया जा सकता है , और कैप्चर किए गए क्षणों की रिपोर्टिंग आउट-ऑफ-ऑर्डर हो सकती है।
Timestamps और उनके आदेश: जब डिबगिंग या जांच कर रही तो, हमेशा यह विचार अपने मन की पीठ में रखा रखने हो सकता है आप पूरा सच नहीं बता रही हो। आप अंततः 100% निश्चितता के साथ नहीं जान सकते कि कब क्या हुआ।