Java8——新时间日期API

本文介绍Java8中新引入的时间日期API,包括LocalDate、LocalTime、LocalDateTime等类的特点与使用方法,以及如何利用这些API进行日期时间的处理,有效避免线程安全问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

 老的时间日期API都存在线程安全问题,因为它们的实例的值都是可变的,多个线程操作同一个实例的时候就会产生线程安全问题。新的时间日期API解决了这个问题,每次改变值都会新创建一个时间日期等实例对象,这样就不存在多个线程操作同一个对象的问题,也就避免了线程安全问题。新的时间日期API都在java.time包下,该包下的实例都是不可变的,也就是说只要变化就会产生新的实例,类似于String,避免了线程安全问题。

线程安全问题示例

 1、多线程日期格式化:多个线程会同时操作同一个DateFormat实例,可能出现线程安全问题

public static void main(String args[]) throws InterruptedException, ExecutionException {
	//多个线程会同时操作该DateFormat实例,就可能出现线程安全问题
	DateFormat df = new SimpleDateFormat("yyyyMMdd");

	Callable<Date> task = new Callable<Date>() {
		@Override
		public Date call() throws Exception {
			return df.parse("20190408");
		}
	};
	//创建线程池
	ExecutorService pool = Executors.newFixedThreadPool(10);
	List<Future<Date>> results = new ArrayList<>();
	for (int i = 0; i < 10; i++) {
		//线程池中的线程执行任务,将结果放在Future中
		results.add(pool.submit(task));
	}

	for (Future<Date> future : results) {
		System.out.println(future.get());
	}

	pool.shutdown();
}

  某一次的运行结果:

Mon Apr 08 00:00:00 CST 2019
Mon Apr 08 00:00:00 CST 2019
Mon Apr 08 00:00:00 CST 2019
Mon Apr 08 00:00:00 CST 2019
Mon Apr 08 00:00:00 CST 2019
Mon Apr 08 00:00:00 CST 2019
Mon Apr 08 00:00:00 CST 2019
Mon Apr 08 00:00:00 CST 2019
Tue Jun 08 00:00:00 CST 2376
Tue Jun 08 00:00:00 CST 1

 2、使用ThreadLocal解决线程安全问题
  ①使用ThreadLocal编写工具类:ThreadLocal为变量在每个线程中都创建一个副本,每个线程只可以访问自己内部的副本变量,避免线程安全问题

public class DateFormatThreadLocal {
	
	private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
		protected DateFormat initialValue() {
			return new SimpleDateFormat("yyyyMMdd");
		}
	};

	public static Date format(String source) throws ParseException {
		return df.get().parse(source);
	}
}

   ThreadLocal的知识参考:深入剖析ThreadLocal
  ②使用本地线程变量中的DateFormat实例:避免线程安全问题

public static void main(String args[]) throws InterruptedException, ExecutionException {

		Callable<Date> task = new Callable<Date>() {
			@Override
			public Date call() throws Exception {
				//使用本地线程变量中的DateFormat实例
				return DateFormatThreadLocal.format("20190408");
			}
		};

		ExecutorService pool = Executors.newFixedThreadPool(10);
		List<Future<Date>> results = new ArrayList<>();
		for (int i = 0; i < 10; i++) {
			results.add(pool.submit(task));
		}

		for (Future<Date> future : results) {
			System.out.println(future.get());
		}

		pool.shutdown();
	}

 3、使用Java8的LocalDate,不需要使用ThreadLocal

public static void main(String args[]) throws InterruptedException, ExecutionException {

	DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");

	Callable<LocalDate> task = new Callable<LocalDate>() {
		@Override
		public LocalDate call() throws Exception {
			return LocalDate.parse("20190408", dtf);
		}
	};

	ExecutorService pool = Executors.newFixedThreadPool(10);
	List<Future<LocalDate>> results = new ArrayList<>();
	for (int i = 0; i < 10; i++) {
		results.add(pool.submit(task));
	}

	for (Future<LocalDate> future : results) {
		System.out.println(future.get());
	}
	pool.shutdown();
}
LocalDate、LocalTime、LocalDateTime

 LocalDate、LocalTime、LocalDateTime 类的实例都是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。(注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法)
 LocalDateTime使用示例:LocalDate和LocalTime类似

// 获取当前时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);

// 获取指定时间
LocalDateTime of = LocalDateTime.of(2019, 4, 9, 12, 23, 35);
System.out.println(of);

// 在之前日期的基础上+2年,产生新的日期时间对象
LocalDateTime plusYears = ldt.plusYears(2);
System.out.println(plusYears);
// 在之前日期的基础上-2月,产生新的日期时间对象
LocalDateTime minusMonths = ldt.minusMonths(2);
System.out.println(minusMonths);

//获取日期
System.out.println(ldt.getDayOfMonth());

  每调用一次API都会产生一个新的实例,而不是改变原有实例,这样就可以避免线程安全问题。

方法描述
now()静态方法,根据当前时间创建对象
of()静态方法,根据指定日期/时间创建
plusDays, plusWeeks, plusMonths, plusYears向当前 LocalDate 对象添加几天、 几周、几个月、几年
minusDays, minusWeeks, minusMonths, minusYears从当前 LocalDate 对象减去几天、 几周、几个月、几年
plus, minus添加或减少一个 Duration或 Period
withDayOfMonth, withDayOfYear, withMonth, withYear将月份天数、年份天数、月份、年 份修改为指定的值并返回新的 LocalDate对象
getDayOfMonth获得月份天数(1-31)
getDayOfYear获得年份天数(1-366)
getDayOfWeek获得星期几(返回一个 DayOfWeek 枚举值)
getMonth获得月份, 返回一个 Month枚举值
getMonthValue获得月份(1-12)
getYear获得年份
until获得两个日期之间的 Period 对象, 或者指定 ChronoUnits的数字
isBefore, isAfter比较两个 LocalDate
isLeapYear判断是否是闰年
时间戳Instant

 时间戳:从Unix元年(1970.01.01 00:00:00)到指定时间的毫秒差值

// 默认获取UTC时区的当前时间,并不是时间戳
Instant ist = Instant.now();
System.out.println(ist);// 2019-04-09T08:08:09.055Z

// 东八区当前时间
OffsetDateTime atOffset = ist.atOffset(ZoneOffset.ofHours(8));
System.out.println(atOffset);// 2019-04-09T16:08:09.055+08:00

// 获取时间戳毫秒数
System.out.println(ist.toEpochMilli());//1554797289055

// 从1970.01.01 00:00:00往后100s
Instant ofEpochSecond = Instant.ofEpochSecond(100);
System.out.println(ofEpochSecond);// 1970-01-01T00:01:40Z
时间间隔Duration和Period

 Duration:两个时间之间的间隔

Instant now = Instant.now();
try {
	Thread.sleep(1000);
} catch (InterruptedException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}
Instant now2 = Instant.now();
//计算时间间隔
Duration between = Duration.between(now, now2);
System.out.println(between.toMillis());

LocalTime lt1 = LocalTime.now();
try {
	Thread.sleep(1000);
} catch (InterruptedException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}
LocalTime lt2 = LocalTime.now();
System.out.println(Duration.between(lt1, lt2).toMillis());

 Period:两个日期之间的间隔

LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2015, 11, 23);
Period between = Period.between(ld2, ld1);
System.out.println(between.getYears());
System.out.println(between.getMonths());
System.out.println(between.getDays());
时间校正器

 TemporalAdjuster:时间校正器。有时我们可能需要获取例如“将日期调整到下个周日”等操作,这时就需要时间校正器。
 TemporalAdjusters:该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。

LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);

//该月10号
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);

//下一个周日
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);

//自定义:下一个工作日
LocalDateTime ldt5 = ldt.with((l) -> {
	LocalDateTime ldt4 = (LocalDateTime) l;
	
	DayOfWeek dow = ldt4.getDayOfWeek();
	
	if(dow.equals(DayOfWeek.FRIDAY)){
		return ldt4.plusDays(3);
	}else if(dow.equals(DayOfWeek.SATURDAY)){
		return ldt4.plusDays(2);
	}else{
		return ldt4.plusDays(1);
	}
});

System.out.println(ldt5);
格式化日期时间

 DateTimeFormatter 类:该类提供了三种格式化方法:
  ①预定义的标准格式
  ②语言环境相关的格式
  ③自定义的格式

// 使用API提供的格式
DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
LocalDateTime ldt = LocalDateTime.now();
// DateTimeFormatter.format(ldt)和LocalDateTime.format(dtf)结果相同
String format = ldt.format(dtf);
System.out.println(format);
String format2 = dtf.format(ldt);
System.out.println(format2);

// 自定义格式
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String format3 = dtf1.format(ldt);
System.out.println(format3);

// 字符串解析为时间日期
LocalDateTime parse = LocalDateTime.parse(format3,dtf1);
System.out.println(parse);
时区的处理

 Java8 中加入了对时区的支持,带时区的日期、时间和日期时间分别对应:ZonedDate、ZonedTime、ZonedDateTime,其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式,例如 :Asia/Shanghai 等
 ZoneId:该类中包含了所有的时区信息
 getAvailableZoneIds():可以获取所有时区信息
 of(id):用指定的时区信息获取 ZoneId 对象

//获取所有时区标识信息
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(System.out::println);

//获取上海所在时区的当前时间
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);

LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(ldt1);

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt);//与LocalDateTime相比多了一个和UTC时区的差值信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值