多线程中的静态方法
Static method in multithreading
我的 class
中有以下代码
private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
public static String getTimeStampAsString(final long time) {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
return SDF_ISO_DATE.format(
new Date(time)) + " " + SDF_ISO_TIME.format(new Date(time)
);
}
在我的多线程应用程序中,以下方法 returns 将来的日期,即使是当前日期,静态方法或变量是否对此负责?
编辑:
我有以下代码来重现和证明答案中提到的内容,但仍然无法to.Can有人帮助我。
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<String> task = new Callable<String>(){
public String call() throws Exception {
return DateUtil.getTimeStampAsString(1524567870569L);
}
};
//pool with 50 threads
ExecutorService exec = Executors.newFixedThreadPool(50);
List<Future<String>> results = new ArrayList<Future<String>>();
//perform 10 date conversions
for(int i = 0 ; i < 50 ; i++){
results.add(exec.submit(task));
}
exec.shutdown();
//look at the results
for(Future<String> result : results){
System.out.println(result.get());
}
}
is the static method or variable is responsible for this?
静态变量。 SimpleDateFormat 不是线程安全的,这一点很明显,因为您正在通过调用 setTimeZone() 修改其内部状态。这意味着多个线程可以同时执行此操作,这应该会产生不可预测的结果。
您需要在本地构建您的格式,而不是重复使用一些静态定义的格式。或者更好的是,放弃 Java 的旧时间管理 类 并使用 java.time.* 代替。
tz
实际上是常量,设置器在第一次调用任一方法后不做任何事情。使用静态初始化程序立即设置时区以使方法线程安全。
private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
static {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
}
public static String getCurrentTimeStamp(final Date date) {
return SDF_ISO_DATE.format(date) + " " + SDF_ISO_TIME.format(date);
}
public static String getTimeStampAsString(final long time) {
return getCurrentTimeStamp(new Date(time));
}
tl;博士
要捕获当前时刻并生成所需格式的字符串(这是标准 ISO 8601 格式的修改形式),请使用 java.time classes。这些 classes 更简单,设计也更好。它们也是线程安全的。
Instant.now().toString().replace( "T" , " " )
当前时刻
您的方法名为 getCurrentTimeStamp(final Date date)
,但您传递的是现有 Date
对象集,而不是捕捉当前时刻。
在你的代码中我没有看到你捕获当前时刻。如果您想要当前时刻,请调用 Instant.now()
,如下所示。
避免遗留日期时间 classes
遗留的日期时间 class 诸如 Date
和 SimpleDateFormat
是 不是 线程安全的。避免这些麻烦的 classes 的众多原因之一。它们在多年前被 java.time classes.
取代
java.time
作为 UTC 时刻,java.util.Date
class 被 Instant
class 取代。同样的想法,但是 Instant
的分辨率是纳秒而不是毫秒。 Instant::toString
不像 Date::toString
那样动态注入时区。
要以 UTC 格式捕获当前时刻,请调用静态 Instant.now()
方法。
Instant instant = Instant.now() ; // Capture current moment in UTC.
将您输入的数字解析为自 UTC 中 1970 年第一时刻的纪元参考以来的毫秒数。
Instant instant = Instant.ofEpochMilli( 1_524_567_870_569L ) ;
instant.toString(): 2018-04-24T11:04:30.569Z
不需要你的代码。不需要您的 DateUtil
,如上面的代码所示。不需要自定义格式模式,因为您想要的格式恰好符合 java.time classes 中默认使用的 ISO 8601 标准。如果中间的 T
打扰到您或您的用户,请替换为 SPACE。
String output = instant.toString().replace( "T" , " " ) ;
2018-04-24T11:04:30.569Z
ExecutorService
阻塞
你好像误会了ExecutorService::shutdown
。该方法 not 阻塞等待任务完成。在您编写代码时,某些任务可能 运行 尚未完成,直到 在您报告结果(部分完成的结果)后 。
添加对 ExecutorService::awaitTermination
的调用,如下面的代码所示。设置一个足够长的超时时间,如果超过它一定意味着发生了一些问题。引用文档:
Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
请参阅下面的示例代码。有关更多讨论,请参阅此问题,
线程
java.timeclasses 是 thread-safe by design. They use the immutable objects 模式,returning 新对象基于现有值而不是更改(“变异”)原件。
示例代码。您的问题对您是想要硬编码时刻还是当前时刻感到困惑。通过在此示例中启用注释掉的行来切换到任一个。
Callable < String > task = new Callable < String >() {
public String call () throws Exception {
long threadId = Thread.currentThread().getId();
// 字符串矩 = Instant.ofEpochMilli( 1524567870569L ).toString().replace( "T" , " " );
String moment = Instant.now().toString().replace( "T" , " " );
字符串输出 = ( moment + " | " + threadId );
return输出;
}
};
// Pool with 5 threads
ExecutorService exec = Executors.newFixedThreadPool( 5 );
List < Future < String > > results = new ArrayList < Future < String > >();
// Perform a certain number of tasks.
int countAssignedTasks = 500;
for ( int i = 0 ; i < countAssignedTasks ; i++ ) {
results.add( exec.submit( task ) );
}
// Wait for tasks to complete.
Boolean completedBeforeTimeOut = null;
try {
exec.shutdown();
completedBeforeTimeOut = exec.awaitTermination( 5 , TimeUnit.SECONDS ); // Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// Report results.
System.out.println( "completedBeforeTimeOut: " + completedBeforeTimeOut );
for ( Future < String > result : results ) {
try {
System.out.println( result.get() );
} catch ( InterruptedException e ) {
e.printStackTrace();
} catch ( ExecutionException e ) {
e.printStackTrace();
}
}
System.out.println( "BASIL - done." );
当运行.
请注意时间是不是时间顺序。在多线程代码中,您无法预测哪些任务将在何时执行。
2018-04-24 20:24:06.991225Z | 13
2018-04-24 20:24:06.991246Z | 14
2018-04-24 20:24:06.991236Z | 15
2018-04-24 20:24:06.991232Z | 16
2018-04-24 20:24:06.991222Z | 17
2018-04-24 20:24:07.067002Z | 16
2018-04-24 20:24:07.067009Z | 17
作为对您编辑的回答:如何重现线程不安全问题(不确定这是否真的应该是一个单独的问题)。在两个或多个线程中使用相同的 SimpleDateFormat
格式化 相同的 日期似乎进展顺利(至少大多数情况下,不能保证它总是会)。尝试格式化不同的日期时间,很容易得到错误的结果。我这样更改了你的任务:
AtomicLong time = new AtomicLong(1_524_567_870_569L);
Callable<String> task = new Callable<String>(){
@Override
public String call() {
return DateUtil.getTimeStampAsString(time.getAndAdd(2_768_461_000L));
}
};
当我在输出中也对它们进行排序时,最容易看出结果是错误的,所以我这样做了。我只引用一个 运行 的前几个结果,因为这足以说明问题:
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-11 13:06:32
2018-07-29 14:07:33
2018-08-08 15:08:34
2018-10-01 16:09:35
…
预期结果是(通过声明 getTimeStampAsString()
同步获得;之后也排序):
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-27 13:06:32
2018-07-29 14:07:33
2018-08-30 15:08:34
2018-10-01 16:09:35
…
第五个打印结果已经将日期全错了,08 而不是 30,而且完整列表中还有更多错误。你可以自己试试。正如您可能知道的那样,确切的结果是不可重现的,但您应该会得到以某种方式出错的结果。
PS这是我的代码,用于按排序顺序打印结果,以防您想尝试:
//look at the results
SortedSet<String> sorted = new TreeSet<>();
for (Future<String> result : results){
sorted.add(result.get());
}
sorted.forEach(System.out::println);
我的 class
中有以下代码private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
public static String getTimeStampAsString(final long time) {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
return SDF_ISO_DATE.format(
new Date(time)) + " " + SDF_ISO_TIME.format(new Date(time)
);
}
在我的多线程应用程序中,以下方法 returns 将来的日期,即使是当前日期,静态方法或变量是否对此负责?
编辑:
我有以下代码来重现和证明答案中提到的内容,但仍然无法to.Can有人帮助我。
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<String> task = new Callable<String>(){
public String call() throws Exception {
return DateUtil.getTimeStampAsString(1524567870569L);
}
};
//pool with 50 threads
ExecutorService exec = Executors.newFixedThreadPool(50);
List<Future<String>> results = new ArrayList<Future<String>>();
//perform 10 date conversions
for(int i = 0 ; i < 50 ; i++){
results.add(exec.submit(task));
}
exec.shutdown();
//look at the results
for(Future<String> result : results){
System.out.println(result.get());
}
}
is the static method or variable is responsible for this?
静态变量。 SimpleDateFormat 不是线程安全的,这一点很明显,因为您正在通过调用 setTimeZone() 修改其内部状态。这意味着多个线程可以同时执行此操作,这应该会产生不可预测的结果。
您需要在本地构建您的格式,而不是重复使用一些静态定义的格式。或者更好的是,放弃 Java 的旧时间管理 类 并使用 java.time.* 代替。
tz
实际上是常量,设置器在第一次调用任一方法后不做任何事情。使用静态初始化程序立即设置时区以使方法线程安全。
private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
static {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
}
public static String getCurrentTimeStamp(final Date date) {
return SDF_ISO_DATE.format(date) + " " + SDF_ISO_TIME.format(date);
}
public static String getTimeStampAsString(final long time) {
return getCurrentTimeStamp(new Date(time));
}
tl;博士
要捕获当前时刻并生成所需格式的字符串(这是标准 ISO 8601 格式的修改形式),请使用 java.time classes。这些 classes 更简单,设计也更好。它们也是线程安全的。
Instant.now().toString().replace( "T" , " " )
当前时刻
您的方法名为 getCurrentTimeStamp(final Date date)
,但您传递的是现有 Date
对象集,而不是捕捉当前时刻。
在你的代码中我没有看到你捕获当前时刻。如果您想要当前时刻,请调用 Instant.now()
,如下所示。
避免遗留日期时间 classes
遗留的日期时间 class 诸如 Date
和 SimpleDateFormat
是 不是 线程安全的。避免这些麻烦的 classes 的众多原因之一。它们在多年前被 java.time classes.
java.time
作为 UTC 时刻,java.util.Date
class 被 Instant
class 取代。同样的想法,但是 Instant
的分辨率是纳秒而不是毫秒。 Instant::toString
不像 Date::toString
那样动态注入时区。
要以 UTC 格式捕获当前时刻,请调用静态 Instant.now()
方法。
Instant instant = Instant.now() ; // Capture current moment in UTC.
将您输入的数字解析为自 UTC 中 1970 年第一时刻的纪元参考以来的毫秒数。
Instant instant = Instant.ofEpochMilli( 1_524_567_870_569L ) ;
instant.toString(): 2018-04-24T11:04:30.569Z
不需要你的代码。不需要您的 DateUtil
,如上面的代码所示。不需要自定义格式模式,因为您想要的格式恰好符合 java.time classes 中默认使用的 ISO 8601 标准。如果中间的 T
打扰到您或您的用户,请替换为 SPACE。
String output = instant.toString().replace( "T" , " " ) ;
2018-04-24T11:04:30.569Z
ExecutorService
阻塞
你好像误会了ExecutorService::shutdown
。该方法 not 阻塞等待任务完成。在您编写代码时,某些任务可能 运行 尚未完成,直到 在您报告结果(部分完成的结果)后 。
添加对 ExecutorService::awaitTermination
的调用,如下面的代码所示。设置一个足够长的超时时间,如果超过它一定意味着发生了一些问题。引用文档:
Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
请参阅下面的示例代码。有关更多讨论,请参阅此问题,
线程
java.timeclasses 是 thread-safe by design. They use the immutable objects 模式,returning 新对象基于现有值而不是更改(“变异”)原件。
示例代码。您的问题对您是想要硬编码时刻还是当前时刻感到困惑。通过在此示例中启用注释掉的行来切换到任一个。
Callable < String > task = new Callable < String >() {
public String call () throws Exception {
long threadId = Thread.currentThread().getId();
// 字符串矩 = Instant.ofEpochMilli( 1524567870569L ).toString().replace( "T" , " " ); String moment = Instant.now().toString().replace( "T" , " " ); 字符串输出 = ( moment + " | " + threadId ); return输出; } };
// Pool with 5 threads
ExecutorService exec = Executors.newFixedThreadPool( 5 );
List < Future < String > > results = new ArrayList < Future < String > >();
// Perform a certain number of tasks.
int countAssignedTasks = 500;
for ( int i = 0 ; i < countAssignedTasks ; i++ ) {
results.add( exec.submit( task ) );
}
// Wait for tasks to complete.
Boolean completedBeforeTimeOut = null;
try {
exec.shutdown();
completedBeforeTimeOut = exec.awaitTermination( 5 , TimeUnit.SECONDS ); // Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// Report results.
System.out.println( "completedBeforeTimeOut: " + completedBeforeTimeOut );
for ( Future < String > result : results ) {
try {
System.out.println( result.get() );
} catch ( InterruptedException e ) {
e.printStackTrace();
} catch ( ExecutionException e ) {
e.printStackTrace();
}
}
System.out.println( "BASIL - done." );
当运行.
请注意时间是不是时间顺序。在多线程代码中,您无法预测哪些任务将在何时执行。
2018-04-24 20:24:06.991225Z | 13
2018-04-24 20:24:06.991246Z | 14
2018-04-24 20:24:06.991236Z | 15
2018-04-24 20:24:06.991232Z | 16
2018-04-24 20:24:06.991222Z | 17
2018-04-24 20:24:07.067002Z | 16
2018-04-24 20:24:07.067009Z | 17
作为对您编辑的回答:如何重现线程不安全问题(不确定这是否真的应该是一个单独的问题)。在两个或多个线程中使用相同的 SimpleDateFormat
格式化 相同的 日期似乎进展顺利(至少大多数情况下,不能保证它总是会)。尝试格式化不同的日期时间,很容易得到错误的结果。我这样更改了你的任务:
AtomicLong time = new AtomicLong(1_524_567_870_569L);
Callable<String> task = new Callable<String>(){
@Override
public String call() {
return DateUtil.getTimeStampAsString(time.getAndAdd(2_768_461_000L));
}
};
当我在输出中也对它们进行排序时,最容易看出结果是错误的,所以我这样做了。我只引用一个 运行 的前几个结果,因为这足以说明问题:
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-11 13:06:32
2018-07-29 14:07:33
2018-08-08 15:08:34
2018-10-01 16:09:35
…
预期结果是(通过声明 getTimeStampAsString()
同步获得;之后也排序):
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-27 13:06:32
2018-07-29 14:07:33
2018-08-30 15:08:34
2018-10-01 16:09:35
…
第五个打印结果已经将日期全错了,08 而不是 30,而且完整列表中还有更多错误。你可以自己试试。正如您可能知道的那样,确切的结果是不可重现的,但您应该会得到以某种方式出错的结果。
PS这是我的代码,用于按排序顺序打印结果,以防您想尝试:
//look at the results
SortedSet<String> sorted = new TreeSet<>();
for (Future<String> result : results){
sorted.add(result.get());
}
sorted.forEach(System.out::println);