了解进程与线程
- 进程:简单的理解——进行中的程序,实际上,进程就是我们操作系统进行资源分配的单位,也是程序运行的载体
- 线程:简单的理解——进程中各种各样的任务,实际上,线程是任务调度的最小单位,一个进程中会有各种的线程在并发执行任务
Java中的线程
- java.lang.Thread 类,即是java中对于线程的封装对象,.我们想要利用多个线程并发执行不同任务,也正是多线程技术,那么就需要使用Thread类对象
构造器方法:
大体而言实际上就两个
-
Thread()
分配新的 Thread 对象。 -
Thread(Runnable target)
分配新的 Thread 对象。
创建Thread线程的方法
-
(1)创建实体类对象继承Thread类并实现run()方法(实际上就是定义了Thread里面的任务)
-
(2)创建实体类对象实现Runnable接口(它就是任务的封装形式)实现其内run方法利用new Thread(Runnable r) 创建Thread对象
-
(3)创建实体类对象实现callable接口创建Thread对象
-
(4)通过匿名内部类直接重写run方法创建Thread对象/Runnable对象(归根结底和前面一样)
多线程的使用
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
ThreadDemo1 a = new ThreadDemo1();
Thread t1 = a.new Mythread();
t1.setPriority(10);
Thread t2 = a.new Mythread();
t1.start();
t2.start();
for(int i= 0 ; i < 20 ; i ++) {
System.out.println(Thread.currentThread().getName()+": "+i);
Thread.sleep(1);
}
System.out.println(Thread.currentThread().getName()+"结束");
}
private class Mythread extends Thread {
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++)
{
System.out.println(Thread.currentThread().getName()+": "+i);
}
System.out.println(Thread.currentThread().getName()+"结束");
}
}
}
运行结果如下:总共三个线程——main、Thread-0、Thread1,三个线程结果看来是交替运行的
main: 0
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结束
Thread-1: 0
Thread-1: 1
Thread-1: 2
Thread-1: 3
Thread-1: 4
Thread-1: 5
main: 1
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-1: 15
Thread-1: 16
Thread-1: 17
Thread-1: 18
Thread-1: 19
Thread-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结束
可能疑惑的问题:
-
多个线程是怎么运行的呢?为什么运行结果会变成随机交替的?
线程首先会处于就绪态,只有就绪态的线程才有权力进入运行态(CPU只会给就绪态分配时间片),什么是时间片——时间片就是这个线程被随机分到的能够占用cpu的时间,分配给谁是随机的,分配多少也是随机的!当一个线程时间片过期就会返还至就绪态,若线程任务完成则会进入死亡态 -
线程重写的不是run方法吗,为什么我们调用的却是start方法?
start方法作用:开启线程使之进入就绪态,等待CPU分配时间片执行run方法,如果我们直接调用run方法,那么就不是在利用线程机制了,这时仅仅只是调用run方法而已!
Callable接口和Runnable接口实现线程的对比
java.util.concurrent 接口 Callable
- Callable接口怎么看看都像是Runable接口的增强版,Callable接口提供了一个call()方法可以作为线程任务执行体,但call()方法比run()方法功能更加强大。
- 1:call()方法可以有返回值
- 2:call()方法可以声明抛出异常
我们发现,Callable接口并不是Runnable接口的子接口,而是一个独立的新接口,对比Runnable取代了void run()方法的是V call() 方法
- V call()
throws Exception
返回计算的结果,如果无法计算结果,则抛出一个异常。
既然Callable不是Runnable的子接口,那么如何构造Thread对象呢?
有这么一个类
- public class FutureTask<V.>implements RunnableFuture<V.>
他实现了Runable接口,因此可以用于构建Thread对象
构造方法摘要
- FutureTask(Callable callable)
创建一个 FutureTask,一旦运行就执行给定的 Callable。
FutureTask(Runnable runnable, V result)
创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。
因此我们可以构造FutureTask对象来构建Thread对象,并通过get方法可以得到任务运行的结果
public class TestCallable {
public static void main(String[] args) {
CallableDemo callableDemo = new CallableDemo();
//执行Callable方式,需要FutureTask实现类的支持,用于接收运算结果
FutureTask<Integer> futureTask = new FutureTask<Integer>(callableDemo);
new Thread(futureTask).start();
Integer sum = futureTask.get();
System.out.println("计算结果:"+sum);
}
}
class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0 ;
for(int i = 0;i <= 100;i++) {
System.out.println(i);
sum += i;
}
return sum;
}
}
守护线程的使用
线程可以分为前置线程和守护线程两类
- 一个进程的结束是进程中所有前置线程的结束,守护线程在前置线程全部转入死亡态时会瞬间被JVM杀死且甚至不会去执行finally语句块内容
public class DaemonTest {
public static void main(String[] args) {
Thread t0 = new Thread() {
public void run() {
while(true) {
System.out.println("守护");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Thread t1 = new Thread() {
public void run() {
for(int i = 0 ; i < 10 ; i ++) {
System.out.println(Thread.currentThread().getName()+": " + i);
}
System.out.println(Thread.currentThread().getName()+"结束");
}
};
Thread t2 = new Thread() {
public void run() {
for(int i = 0 ; i < 10 ; i ++)
{
System.out.println(Thread.currentThread().getName()+": " + i);
}
System.out.println(Thread.currentThread().getName()+"结束");
}
};
t0.setDaemon(true);
t0.start();
t1.start();
t2.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结束
Thread-2: 0
Thread-2: 1
Thread-2: 2
Thread-2: 3
Thread-2: 4
Thread-2: 5
Thread-2: 6
Thread-2: 7
Thread-2: 8
Thread-2: 9
Thread-2结束