日期与工夫
Date 和 Calendar
计算机示意的工夫是以整数示意的工夫戳存储的,即 Epoch Time,Java 应用 long
型来示意以毫秒为单位的工夫戳,通过 System.currentTimeMillis()
获取以后工夫戳。
Java 有两套日期和工夫的 API:
- 旧的 Date、Calendar 和 TimeZone;
- 新的 LocalDateTime、ZonedDateTime、ZoneId 等。
别离位于 java.util
和java.time
包中。
Date
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
Calendar
能够用于获取并设置年、月、日、时、分、秒,它和 Date
比,次要多了一个能够做简略的日期和工夫运算的性能。
LocalDateTime
从 Java 8 开始,java.time
包提供了新的日期和工夫 API,次要波及的类型有:
- 本地日期和工夫:
LocalDateTime
,LocalDate
,LocalTime
; - 带时区的日期和工夫:
ZonedDateTime
; - 时刻:
Instant
; - 时区:
ZoneId
,ZoneOffset
; - 工夫距离:
Duration
。
以及一套新的用于取代 SimpleDateFormat
的格式化类型DateTimeFormatter
。
和旧的 API 相比,新 API 严格辨别了时刻、本地日期、本地工夫和带时区的日期工夫,并且,对日期和工夫进行运算更加不便。
此外,新 API 修改了旧 API 不合理的常量设计:
- Month 的范畴用 1~12 示意 1 月到 12 月;
- Week 的范畴用 1~7 示意周一到周日。
最初,新 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 ││
│└────────┘││└────────┘││└────────┘│
└──────────┘└──────────┘└──────────┘
过程 vs 线程
过程和线程是蕴含关系,然而多任务既能够由多过程实现,也能够由单过程内的多线程实现,还能够混合多过程+多线程。
具体采纳哪种形式,要思考到过程和线程的特点。
和多线程相比,多过程的毛病在于:
- 创立过程比创立线程开销大,尤其是在 Windows 零碎上;
- 过程间通信比线程间通信要慢,因为线程间通信就是读写同一个变量,速度很快。
而多过程的长处在于:
多过程稳定性比多线程高,因为在多过程的状况下,一个过程解体不会影响其余过程,而在多线程的状况下,任何一个线程解体会间接导致整个过程解体。
多线程
Java 语言内置了多线程反对:一个 Java 程序实际上是一个 JVM 过程,JVM 过程用一个主线程来执行 main()
办法,在 main()
办法外部,咱们又能够启动多个线程。此外,JVM 还有负责垃圾回收的其余工作线程等。
因而,对于大多数 Java 程序来说,咱们说多任务,实际上是说如何应用多线程实现多任务。
和单线程相比,多线程编程的特点在于:多线程常常须要读写共享数据,并且须要同步。例如,播放电影时,就必须由一个线程播放视频,另一个线程播放音频,两个线程须要协调运行,否则画面和声音就不同步。因而,多线程编程的复杂度高,调试更艰难。
Java 多线程编程的特点又在于:
- 多线程模型是 Java 程序最根本的并发模型;
- 后续读写网络、数据库、Web 开发等都依赖 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 线程的状态有以下几种:
- New:新创建的线程,尚未执行;
- Runnable:运行中的线程,正在执行
run()
办法的 Java 代码; - Blocked:运行中的线程,因为某些操作被阻塞而挂起;
- Waiting:运行中的线程,因为某些操作在期待中;
- Timed Waiting:运行中的线程,因为执行
sleep()
办法正在计时期待; - Terminated:线程已终止,因为
run()
办法执行结束。
Maven 根底
Maven 是一个 Java 项目管理和构建工具,它能够定义我的项目构造、我的项目依赖,并应用对立的形式进行自动化构建,是 Java 我的项目不可短少的工具。