计算机表示的时间是以整数表示的时间戳存储的,即Epoch Time,Java使用long
型来表示以毫秒为单位的时间戳,经过System.currentTimeMillis()
获取当前时间戳。java
Java有两套日期和时间的API:sql
分别位于java.util
和java.time
包中。数据库
java.util.Date
是用于表示一个日期和时间的对象,注意与java.sql.Date
区分,后者用在数据库中。若是观察Date的源码,能够发现它实际上存储了一个long类型的以毫秒表示的时间戳编程
// 获取当前时间: Date date = new Date(); System.out.println(date.getYear() + 1900); // 必须加上1900 System.out.println(date.getMonth() + 1); // 0~11,必须加上1 System.out.println(date.getDate()); // 1~31,不能加1 // 转换为String: System.out.println(date.toString()); // 转换为GMT时区: System.out.println(date.toGMTString()); // 转换为本地时区: System.out.println(date.toLocaleString());
注意getYear()
返回的年份必须加上1900
,getMonth()
返回的月份是0
~11
分别表示1~12月,因此要加1,而getDate()
返回的日期范围是1
~31
,又不能加1。浏览器
打印本地时区表示的日期和时间时,不一样的计算机可能会有不一样的结果。若是咱们想要针对用户的偏好精确地控制日期和时间的格式,就可使用SimpleDateFormat
对一个Date
进行转换。网络
Calendar
能够用于获取并设置年、月、日、时、分、秒,它和Date
比,主要多了一个能够作简单的日期和时间运算的功能。多线程
从Java 8开始,java.time
包提供了新的日期和时间API,主要涉及的类型有:并发
LocalDateTime
,LocalDate
,LocalTime
;ZonedDateTime
;Instant
;ZoneId
,ZoneOffset
;Duration
。以及一套新的用于取代SimpleDateFormat
的格式化类型DateTimeFormatter
。ide
和旧的API相比,新API严格区分了时刻、本地日期、本地时间和带时区的日期时间,而且,对日期和时间进行运算更加方便。工具
此外,新API修正了旧API不合理的常量设计:
最后,新API的类型几乎所有是不变类型(和String相似),能够放心使用没必要担忧被修改。
多线程是Java最基本的一种并发模型
现代操做系统(Windows,macOS,Linux)均可以执行多任务。多任务就是同时运行多个任务,例如:
CPU执行代码都是一条一条顺序执行的,可是,即便是单核cpu,也能够同时运行多个任务。由于操做系统执行多任务实际上就是让CPU对多个任务轮流交替执行。
例如,假设咱们有语文、数学、英语3门做业要作,每一个做业须要30分钟。咱们把这3门做业当作是3个任务,能够作1分钟语文做业,再作1分钟数学做业,再作1分钟英语做业,这样轮流作下去,在某些人眼里看来,作做业的速度就很是快,看上去就像同时在作3门做业同样。
相似的,操做系统轮流让多个任务交替执行,例如,让浏览器执行0.001秒,让QQ执行0.001秒,再让音乐播放器执行0.001秒,在人看来,CPU就是在同时执行多个任务。
即便是多核CPU,由于一般任务的数量远远多于CPU的核数,因此任务也是交替执行的。
在计算机中,咱们把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另外一个进程,相似的,音乐播放器和Word都是进程。
某些进程内部还须要同时执行多个子任务。例如,咱们在使用Word时,Word可让咱们一边打字,一边进行拼写检查,同时还能够在后台进行打印,咱们把子任务称为线程。
进程和线程的关系就是:一个进程能够包含一个或多个线程,但至少会有一个线程。
┌──────────┐ │Process │ │┌────────┐│ ┌──────────┐││ Thread ││┌──────────┐ │Process ││└────────┘││Process │ │┌────────┐││┌────────┐││┌────────┐│ ┌──────────┐││ Thread ││││ Thread ││││ Thread ││ │Process ││└────────┘││└────────┘││└────────┘│ │┌────────┐││┌────────┐││┌────────┐││┌────────┐│ ││ Thread ││││ Thread ││││ Thread ││││ Thread ││ │└────────┘││└────────┘││└────────┘││└────────┘│ └──────────┘└──────────┘└──────────┘└──────────┘ ┌──────────────────────────────────────────────┐ │ Operating System │ └──────────────────────────────────────────────┘
操做系统调度的最小任务单位其实不是进程,而是线程。经常使用的Windows、Linux等操做系统都采用抢占式多任务,如何调度线程彻底由操做系统决定,程序本身不能决定何时执行,以及执行多长时间。
由于同一个应用程序,既能够有多个进程,也能够有多个线程,所以,实现多任务的方法,有如下几种:
多进程模式(每一个进程只有一个线程):
┌──────────┐ ┌──────────┐ ┌──────────┐ │Process │ │Process │ │Process │ │┌────────┐│ │┌────────┐│ │┌────────┐│ ││ Thread ││ ││ Thread ││ ││ Thread ││ │└────────┘│ │└────────┘│ │└────────┘│ └──────────┘ └──────────┘ └──────────┘
多线程模式(一个进程有多个线程):
┌────────────────────┐ │Process │ │┌────────┐┌────────┐│ ││ Thread ││ Thread ││ │└────────┘└────────┘│ │┌────────┐┌────────┐│ ││ Thread ││ Thread ││ │└────────┘└────────┘│ └────────────────────┘
多进程+多线程模式(复杂度最高):
┌──────────┐┌──────────┐┌──────────┐ │Process ││Process ││Process │ │┌────────┐││┌────────┐││┌────────┐│ ││ Thread ││││ Thread ││││ Thread ││ │└────────┘││└────────┘││└────────┘│ │┌────────┐││┌────────┐││┌────────┐│ ││ Thread ││││ Thread ││││ Thread ││ │└────────┘││└────────┘││└────────┘│ └──────────┘└──────────┘└──────────┘
进程和线程是包含关系,可是多任务既能够由多进程实现,也能够由单进程内的多线程实现,还能够混合多进程+多线程。
具体采用哪一种方式,要考虑到进程和线程的特色。
和多线程相比,多进程的缺点在于:
而多进程的优势在于:
多进程稳定性比多线程高,由于在多进程的状况下,一个进程崩溃不会影响其余进程,而在多线程的状况下,任何一个线程崩溃会直接致使整个进程崩溃。
Java语言内置了多线程支持:一个Java程序其实是一个JVM进程,JVM进程用一个主线程来执行main()
方法,在main()
方法内部,咱们又能够启动多个线程。此外,JVM还有负责垃圾回收的其余工做线程等。
所以,对于大多数Java程序来讲,咱们说多任务,其实是说如何使用多线程实现多任务。
和单线程相比,多线程编程的特色在于:多线程常常须要读写共享数据,而且须要同步。例如,播放电影时,就必须由一个线程播放视频,另外一个线程播放音频,两个线程须要协调运行,不然画面和声音就不一样步。所以,多线程编程的复杂度高,调试更困难。
Java多线程编程的特色又在于:
所以,必须掌握Java多线程编程才能继续深刻学习其余内容。
Java语言内置了多线程支持。当Java程序启动的时候,其实是启动了一个JVM进程,而后,JVM启动主线程来执行main()
方法。在main()
方法中,咱们又能够启动其余线程。
要建立一个新线程很是容易,咱们须要实例化一个Thread
实例,而后调用它的start()
方法:
public class Main { public static void main(String[] args) { Thread t = new Thread(); t.start(); // 启动新线程 } }
可是这个线程启动后实际上什么也不作就马上结束了。咱们但愿新线程能执行指定的代码,有如下几种方法:
方法一:从Thread
派生一个自定义类,而后覆写run()
方法:
public class Main { public static void main(String[] args) { Thread t = new MyThread(); t.start(); // 启动新线程 } } class MyThread extends Thread { @Override public void run() { System.out.println("start new thread!"); } }
执行上述代码,注意到start()
方法会在内部自动调用实例的run()
方法。
方法二:建立Thread
实例时,传入一个Runnable
实例:
public class Main { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); // 启动新线程 } } class MyRunnable implements Runnable { @Override public void run() { System.out.println("start new thread!"); } }
或者用Java8引入的lambda语法进一步简写为:
public class Main { public static void main(String[] args) { Thread t = new Thread(() -> { System.out.println("start new thread!"); }); t.start(); // 启动新线程 } }
Java用Thread
对象表示一个线程,经过调用start()
启动一个新线程;
一个线程对象只能调用一次start()
方法;
线程的执行代码写在run()
方法中;
线程调度由操做系统决定,程序自己没法决定调度顺序;
Thread.sleep()
能够把当前线程暂停一段时间。
在Java程序中,一个线程对象只能调用一次start()
方法启动新线程,并在新线程中执行run()
方法。一旦run()
方法执行完毕,线程就结束了。所以,Java线程的状态有如下几种:
run()
方法的Java代码;sleep()
方法正在计时等待;run()
方法执行完毕。Maven是一个Java项目管理和构建工具,它能够定义项目结构、项目依赖,并使用统一的方式进行自动化构建,是Java项目不可缺乏的工具。