进程
概念:
- 程序中的一次执行过程,也是系统运行程序的基本单位,因此进程是动态的。
- 在Java中,当我们启动main函数时其实已经启动了一个JVM的进程,因此我们创建的main就是所在这个进程中的一个线程,也称为主线程。
线程
概念:
- 线程是一个程序内部的顺序控制流
- 线程与进程相似,是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。
生命周期:
-
新建状态: 一个新产生的线程从新状态开始了它的生命周期。它保持这个状态直到程序start这个线程。
-
**就绪状态:**当一个线程等待另外一个线程执行一个任务的时候,该线程就进入就绪状态。当另一个线程给就绪状态的线程发送信号时,该线程才重新切换到运行状态。
-
**运行状态:**当一个新状态的线程被start以后,线程就变成可运行状态,一个线程在此状态下被认为是开始执行其任务
-
休眠状态: 由于一个线程的时间片用完了,该线程从运行状态进入休眠状态。当时间间隔到期或者等待的事件发生了,该状态的线程切换到运行状态。
-
终止状态: 一个运行状态的线程完成任务或者其他终止条件发生,该线程就切换到终止状态。
线程与进程的区别
- 每个线程都有独立的代码和数据空间(进程上下文),进程之间的切换会有较大的开销。
- 线程可以看作轻量级进程,同一类的线程共享代码与数据空间,每个线程都有独立运行虚拟栈和程序计数器(PC),由此可见线程切换开销更小。
- 多进程:在操作系统中同时运行多个任务(程序)。
- 多线程:在同一个应用程序中有多个顺序流同时执行。
线程的实现方法
- Java线程是通过 java.lang.Thread 类实现的,因此可以通过创建Thread实例或继承Thread类来创建新的线程。
/**
* 通过继承Thread类来创建线程
* @author Juniors
*/
public class TestThreadInit1 {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
static class MyThread extends Thread{
@Override
public void run() {
System.out.println("Thread body");
}
}
}
- 通过实现Runnable接口,并实现接口的run()方法,再用实现Runnable接口的对象作为参数实例化该Thread对象。
/**
* 通过实现Runnable接口创建线程
* @author Juniors
*/
public class TestThreadInit2 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("RunnableThread body");
}
}
}
- 实现Callable接口,重写call()方法。
- Callable接口实际属于Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能,主要体现以下三点:
1* :Callable可以在任务结束后提供一个返回值,但Runnable无法提供这个功能。
2* :Callable中的call()方法可以抛出异常,而Runnable中run()方法不能抛出异常。
3* :运行Callable可以拿到一个Future对象,该对象可以表示异步计算的结果,他提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下,就可以使用Future来监视目标线程调用call()方法的情况,当调用Future的get()方法以获取结果时,当前线程就会阻塞,直到call()方法结束返回结果。
public class TestThreadInit3 {
public static class CallableTest implements Callable<String>{
@Override
public String call() throws Exception {
return "Hello Juniors!";
}
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
//启动线程
Future<String> future = threadPool.submit(new CallableTest());
try {
System.out.println("waiting thread to finish");
//等待线程结束,并获取返回结果
System.out.println(future.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
三种创建线程方法比较与推荐
以上三种方式中,前两种方式线程执行完后没有返回值,只有最后一种带返回值的。
当需要实现多线程时,一般推荐实现Runnable接口的方式,原因是:通常一个类仅在他们需要被加强或修改时才会被继承,因为Java仅支持单继承。
run()方法与start()方法有什么区别?
- 通常系统调用线程类的start()方法启动一个线程,此时该线程处于就绪状态,而非运行状态,也就意味着这个线程可以被JVM调度执行。在调度的过程中,JVM调用线程类中的run()方法来完成实际操作,当run()方法结束后,此线程就会终止。
- 如果直接调用线程类的run方法,则会当作一个普通函数的调用,程序中仍然只有主线程这一个线程。也就是说start()方法可以异步调用run()方法,但是直接调用run()方法却是同步的,因此也就无法达到实现多线程的目的。
start()方法的使用实例:
/**
* 方法调用 与 线程启动 的区别
* start()方法的使用
* @author Juniors
*/
public class TestThreadInit1 {
public static void main(String[] args) {
Runner runner = new Runner();
//runner.run();
Thread thread = new Thread(runner);
thread.start();
for(int i = 1;i < 10;i++){
System.out.println("Main----"+i);
}
}
static class Runner implements Runnable{
@Override
public void run() {
for(int i = 1;i < 5;i++){
System.out.println("Runner----"+i);
}
}
}
}
程序运行结果:
run()方法使用实例:
/**
* 方法调用 与 线程启动 的区别
* run()方法使用
* @author Juniors
*/
public class TestThreadInit1 {
public static void main(String[] args) {
Runner runner = new Runner();
runner.run();
for(int i = 1;i < 10;i++){
System.out.println("Main----"+i);
}
}
static class Runner implements Runnable{
@Override
public void run() {
for(int i = 1;i < 5;i++){
System.out.println("Runner----"+i);
}
}
}
}
程序运行结果:
待更。。。。。