这个系列开始来说解 Java 多线程的知识,这节就先讲解多线程的基本知识。bash
进程就是在运行过程当中的程序,就好像手机运行中的微信,QQ,这些就叫作进程。微信
线程就是进程的执行单元,就好像一个音乐软件能够听音乐,下载音乐,这些任务都是由线程来完成的。多线程
Java 中建立线程的方法有三种,如下来逐一详细讲解。并发
使用继承 Thread 类建立线程的步骤以下:ide
代码举例以下:post
public class ThreadDemo extends Thread {
// 1. 新建一个类继承 Thread 类,并重写 Thread 类的 run() 方法。
@Override
public void run() {
System.out.println("Hello Thread");
}
public static void main(String[] args) {
// 2. 建立 Thread 子类的实例。
ThreadDemo threadDemo = new ThreadDemo();
// 3. 调用该子类实例的 start() 方法启动该线程。
threadDemo.start();
}
}
复制代码
打印结果以下:ui
Hello Thread
复制代码
使用实现 Runnable 接口建立线程步骤是:spa
代码举例以下:线程
public class RunnableDemo implements Runnable {
// 1. 建立一个类实现 Runnable 接口,并重写该接口的 run() 方法。
@Override
public void run() {
System.out.println("Hello Runnable");
}
public static void main(String[] args) {
// 2. 建立该实现类的实例。
RunnableDemo runnableDemo = new RunnableDemo();
// 3. 将该实例传入 Thread(Runnable r) 构造方法中建立 Thread 实例。
Thread thread = new Thread(runnableDemo);
// 4. 调用该 Thread 线程对象的 start() 方法。
thread.start();
}
}
复制代码
打印结果以下:code
Hello Runnable
复制代码
使用这种方法建立的线程能够获取一个返回值,使用实现 Callable 和 FutureTask 建立线程步骤是:
代码举例以下:
public class CallableDemo implements Callable<String> {
// 1. 建立一个类实现 Callable 接口,并重写 call() 方法。
@Override
public String call() throws Exception {
System.out.println("CallableDemo is Running");
return "Hello Callable";
}
public static void main(String[] args) {
// 2. 建立该 Callable 接口实现类的实例。
CallableDemo callableDemo = new CallableDemo();
// 3. 将 Callable 的实现类实例传入 FutureTask(Callable<V> callable) 构造方法中建立 FutureTask 实例。
FutureTask<String> futureTask = new FutureTask<>(callableDemo);
// 4. 将 FutureTask 实例传入 Thread(Runnable r) 构造方法中建立 Thread 实例。
Thread thread = new Thread(futureTask);
// 5. 调用该 Thread 线程对象的 start() 方法。
thread.start();
// 6. 调用 FutureTask 实例对象的 get() 方法获取返回值。
try {
System.out.println(futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
复制代码
打印结果以下:
CallableDemo is Running
Hello Callable
复制代码
当一个线程开启以后,它会遵循必定的生命周期,它要通过新建,就绪,运行,阻塞和死亡这五种状态,理解线程的生命周期有助于理解后面的相关的线程知识。
这个状态的意思就是线程刚刚被建立出来,这时候的线程并无任何线程的动态特征。
当线程对象调用 start() 方法后,该线程就处于就绪状态。处于这个状态中的线程并无开始运行,只是表示这个线程能够运行了。
处于就绪状态的线程得到了 CPU 后,开始执行 run() 方法,这个线程就处于运行状态。
当线程被暂停后,这个线程就处于阻塞状态。
当线程被中止后,这个线程就处于死亡状态。
其实掌握多线程最主要的就是要熟悉控制线程的状态,让各个线程能更好的为咱们的服务,下面就来说解控制线程的方法。
public static native void sleep(long millis)
public static void sleep(long millis, int nanos)
复制代码
该方法的意思就是让正在运行状态的线程到阻塞状态,而这个时间就是线程处于阻塞状态的时间。millis 是毫秒的意思,nanos 是毫微秒。
public class SleepDemo {
public static void main(String[] args) throws Exception {
for(int i = 0; i < 10; i++) {
System.out.println("Hello Thread Sleep");
Thread.sleep(1000);
}
}
}
复制代码
以上代码运行后每隔一秒就输出 Hello Thread Sleep。
public final void setPriority(int newPriority)
public final int getPriority()
复制代码
从方法名就能够知道,以上两个方法分别就是设置和得到优先级的。值得注意的是优先级是在 1~10 范围内,也可使用如下三个静态变量设置:
public static native void yield();
复制代码
这个方法的意思就是让正在运行的线程回到就绪状态,并不会阻塞线程。可能会发生一种状况就是,该线程调用了 yield() 方法后,线程调度器又会继续调用该线程。 这个方法要注意的是它只会让步给比它优先级高的或者和它优先级相同并处在就绪状态的线程。
public class YieldDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(getName() + " " + i);
if (i == 20) {
Thread.yield();
}
}
}
public static void main(String[] args) {
YieldDemo yieldDemo1 = new YieldDemo();
YieldDemo yieldDemo2 = new YieldDemo();
yieldDemo1.start();
yieldDemo2.start();
}
}
复制代码
代码输出结果:
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-1 10
Thread-1 11
Thread-1 12
Thread-1 13
Thread-1 14
Thread-0 0
Thread-0 1
Thread-0 2
Thread-1 15
Thread-0 3
Thread-1 16
Thread-1 17
Thread-1 18
Thread-1 19
Thread-1 20
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-1 21
Thread-0 8
Thread-1 22
Thread-0 9
Thread-1 23
Thread-0 10
Thread-0 11
Thread-0 12
Thread-1 24
Thread-1 25
Thread-0 13
Thread-1 26
Thread-1 27
Thread-0 14
Thread-0 15
Thread-1 28
Thread-0 16
Thread-0 17
Thread-0 18
Thread-1 29
Thread-0 19
Thread-0 20
Thread-1 30
Thread-1 31
Thread-0 21
Thread-1 32
Thread-1 33
Thread-1 34
Thread-1 35
Thread-1 36
Thread-1 37
Thread-1 38
Thread-1 39
Thread-1 40
Thread-1 41
Thread-1 42
Thread-1 43
Thread-1 44
Thread-1 45
Thread-1 46
Thread-1 47
Thread-1 48
Thread-1 49
Thread-0 22
Thread-0 23
Thread-0 24
Thread-0 25
Thread-0 26
Thread-0 27
Thread-0 28
Thread-0 29
Thread-0 30
Thread-0 31
Thread-0 32
Thread-0 33
Thread-0 34
Thread-0 35
Thread-0 36
Thread-0 37
Thread-0 38
Thread-0 39
Thread-0 40
Thread-0 41
Thread-0 42
Thread-0 43
Thread-0 44
Thread-0 45
Thread-0 46
Thread-0 47
Thread-0 48
Thread-0 49
复制代码
从打印结果就能够看到打印 Thread-1 20的时候,下一个执行的就是 Thread-0 4。打印 Thread-20 的时候,下一个执行的就是 Thread-1 30。 可是要说明的是,不是每次的打印结果都是同样的,由于前面说过线程调用 yield() 方法后,线程调度器有可能会继续启动该线程。
public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
复制代码
这个方法其实要有两个线程,也就是一个线程的线程执行体中有另外一个线程在调用 join() 方法。举个例子,Thread1 的 run() 方法执行体中有 Thread2 在调用 join(),这时候 Thread1 就会被阻塞,必需要等到 Thread2 的线程执行完成或者 join() 方法的时间到后才会继续执行。
public class JoinDemo extends Thread {
@Override
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) throws Exception {
JoinDemo joinDemo = new JoinDemo();
for(int i = 0; i < 50; i++) {
if(i == 20) {
joinDemo.start();
joinDemo.join();
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
复制代码
代码输出的结果:
main 0
main 1
main 2
main 3
main 4
main 5
main 6
main 7
main 8
main 9
main 10
main 11
main 12
main 13
main 14
main 15
main 16
main 17
main 18
main 19
main 20
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-0 10
Thread-0 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19
Thread-0 20
Thread-0 21
Thread-0 22
Thread-0 23
Thread-0 24
Thread-0 25
Thread-0 26
Thread-0 27
Thread-0 28
Thread-0 29
Thread-0 30
Thread-0 31
Thread-0 32
Thread-0 33
Thread-0 34
Thread-0 35
Thread-0 36
Thread-0 37
Thread-0 38
Thread-0 39
Thread-0 40
Thread-0 41
Thread-0 42
Thread-0 43
Thread-0 44
Thread-0 45
Thread-0 46
Thread-0 47
Thread-0 48
Thread-0 49
main 21
main 22
main 23
main 24
main 25
main 26
main 27
main 28
main 29
main 30
main 31
main 32
main 33
main 34
main 35
main 36
main 37
main 38
main 39
main 40
main 41
main 42
main 43
main 44
main 45
main 46
main 47
main 48
main 49
复制代码
以上的代码其实一个两个线程,一个是 Thread-0,另外一个就是 main,main 就是主线程的意思。从打印结果能够看到,主线程执行到 main 20 的时候,就开始执行 Thread-0 0,直到 Thread-0 执行完毕,main 才继续执行。
public class JoinDemo extends Thread {
@Override
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) throws Exception {
JoinDemo joinDemo = new JoinDemo();
for(int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if(i == 20) {
joinDemo.start();
joinDemo.join(1);
}
}
}
}
复制代码
打印结果:
main 0
main 1
main 2
main 3
main 4
main 5
main 6
main 7
main 8
main 9
main 10
main 11
main 12
main 13
main 14
main 15
main 16
main 17
main 18
main 19
main 20
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-0 10
Thread-0 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19
Thread-0 20
Thread-0 21
Thread-0 22
Thread-0 23
Thread-0 24
Thread-0 25
Thread-0 26
Thread-0 27
Thread-0 28
Thread-0 29
Thread-0 30
Thread-0 31
Thread-0 32
Thread-0 33
main 21
main 22
Thread-0 34
main 23
Thread-0 35
Thread-0 36
main 24
main 25
main 26
Thread-0 37
main 27
Thread-0 38
main 28
Thread-0 39
main 29
Thread-0 40
main 30
Thread-0 41
main 31
Thread-0 42
main 32
main 33
main 34
main 35
Thread-0 43
Thread-0 44
Thread-0 45
main 36
Thread-0 46
main 37
main 38
main 39
main 40
main 41
main 42
main 43
main 44
main 45
main 46
main 47
main 48
main 49
Thread-0 47
Thread-0 48
Thread-0 49
复制代码
其实这个的代码和 4.4.3 节的代码基本同样,就是将 join() 改为 join(1) ,能够看到 main 并无等到 Thread-0 执行完就开始从新执行了。
public final void setDaemon(boolean on)
public final boolean isDaemon()
复制代码
这个方法就是将线程设置为后台线程,后台线程的特色就是当前台线程所有执行结束后,后台线程就会随之结束。此方法设置为 true 时,就是将线程设置为后台线程。 而 isDaemon() 就是返回此线程是否为后台线程。
public class DaemonDemo extends Thread {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(getName() + " "+ isDaemon() + " " + i);
}
}
public static void main(String[] args) {
DaemonDemo daemonDemo = new DaemonDemo();
daemonDemo.setDaemon(true);
daemonDemo.start();
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
复制代码
打印结果:
main 0
main 1
main 2
main 3
main 4
Thread-0 true 0
main 5
main 6
main 7
main 8
main 9
Thread-0 true 1
Thread-0 true 2
Thread-0 true 3
Thread-0 true 4
Thread-0 true 5
Thread-0 true 6
Thread-0 true 7
Thread-0 true 8
Thread-0 true 9
Thread-0 true 10
Thread-0 true 11
复制代码
从打印结果能够看到 main 执行完后,Thread-0 没有执行完毕就结束了。
做用处 | sleep() | yield() |
---|---|---|
给其余线程执行机会 | 会给其余线程执行机会,不会理会其余线程的优先级 | 只会给优先级相同,或者优先级更高的线程执行机会 |
影响当前线程的状态 | 从阻塞到就绪状态 | 直接进入就绪状态 |
异常 | 须要抛出 InterruptedException | 不须要抛出任何异常 |
多线程系列文章: