多进程和多线程的概念


进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。凡是用于完成操作系统的各种功能的进程就是系统进程,而所有由你启动的进程都是用户进程。

多进程

进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。凡是用于完成操作系统的各种功能的进程就是系统进程,而所有由你启动的进程都是用户进程。
多进程和多线程的概念 - 图1
如图所示每一个正在运行的 .exe 程序都是一个进程。

多线程

进程就是有一个或多个线程构成的。而线程是进程中的实际运行单位,是独立运行于进程之中的子任务。是操作系统进行运算调度的最小单位。可理解为线程是进程中的一个最小运行单元。

进程和线程之间的关系

一个进程下包含 N 个线程。
举例说明:玩英雄联盟的时候,打开客户端便启动了许多个线程:排队队列线程、好友聊天线程、正在支付线程。在英雄联盟这一个进程之下便启动了 N 个线程。
我们初学 java 边写代码的时候,通常使用 main 方法进行运行,此时 main 方法执行的便是一个主线程,而所谓的多线程,即是在主线程执行的过程中,同时执行其他的线程。
但是同时执行多个线程容易出现报错现象,例如同时同分同秒,两个线程同时修改一个 txt、数据库表文件,或第一个线程没有修改完 txt、数据库表文件,第二个线程同时也去修改。这便是线程之间的混乱、资源竞争、脏读,便是程序员需要去解决的疑难杂症。

创建多线程——继承 Thread

java 世界中有两种方式创建多线程

java 世界中有两种方式创建多线程,分别是继承 Thread 类,实现 Runnable 接口。

继承 Thread 类方式创建多线程

第一步:在 webide 上右键单击菜单,选择 New File 创建新文件。
多进程和多线程的概念 - 图2
第二步:创建文件 test0.java。
多进程和多线程的概念 - 图3
第三步:编写 test0.java 中继承 Thread 类方式创建多线程的代码如下所示:
public class test0 { public static void main(String[] args) { Thread MyThread = new MyThread(); MyThread.start(); } } class MyThread extends Thread { @Override public void run() { System.out.println(“hello myThread” + Thread.currentThread().getName()); } }
第四步:编译 test0.java 代码:
javac test0.java
多进程和多线程的概念 - 图4
编译之后,会产生我们所编写的 test0 类与 MyThread 类
多进程和多线程的概念 - 图5
第五步:运行 test 代码:
java test0
多进程和多线程的概念 - 图6

创建多线程——实现 Runnable


只需要把《创建多线程——继承 Thread》中代码修改成如下所示即可,其它操作不变:

  1. public class test0 {
  2. public static void main(String[] args) {
  3. MyRunnable myRunnable = new MyRunnable();
  4. Thread thread = new Thread(myRunnable);
  5. thread.start();
  6. }
  7. }
  8. class MyRunnable implements Runnable{
  9. @Override
  10. public void run(){
  11. System.out.println("hello myRunnable" + Thread.currentThread().getName());
  12. }
  13. }

public class test0 { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); } } class MyRunnable implements Runnable{ @Override public void run(){ System.out.println(“hello myRunnable” + Thread.currentThread().getName()); } }
执行结果如下所示:
多进程和多线程的概念 - 图7
通常情况下,如果创建的线程类已经含有父类时候,此时由于 Java 语法结构不支持多继承的原因,不能够再次继承 Thread 类,此时则需要使用实现 Runnable 接口的方式来应对如此场景。
另外值得说明的是,Thread 类也实现了 Runnable 接口。
多进程和多线程的概念 - 图8

实现多线程传参——有参构造


由于多线程是由继承 Thread 或实现 Runnable 并重写 run() 方法,通过 thread.start() 进行运行的,而本身重写的 run() 方法是不具备传参能力的,那我新建的线程就接受不到我所想传入的参数了么?

创建 study1.java 文件

  1. class ThreadA extends Thread{
  2. private String age;
  3. public ThreadA(String age){
  4. this.age = age;
  5. }
  6. @Override
  7. public void run() {
  8. System.out.println("age=" + age);
  9. }
  10. }
  11. public class study1 {
  12. public static void main(String[] args) {
  13. String age = new String("12");
  14. ThreadA a = new ThreadA(age);
  15. a.start();
  16. }
  17. }

class ThreadA extends Thread{ private String age; public ThreadA(String age){ this.age = age; } @Override public void run() { System.out.println(“age=” + age); } } public class study1 { public static void main(String[] args) { String age = new String(“12”); ThreadA a = new ThreadA(age); a.start(); } }
无论 extends Thread 还是 implements Runnable ,传参都需要使用线程初始化的有参构造形式,达到多线程传参的目的。也可以做到重载有参构造,传入各式对象。

study1 运行结果

多进程和多线程的概念 - 图9

实现多线程返回值——实现 Callable


通常意义上理解确实 Java 实现多线程的方式有继承 Thread 和实现 Runnable,但是如果想实现多线程并且具有返回值的情况下,需要实现 Callable 接口,这个接口是 JDK1.5 版本以后才出现的接口。

创建 study2.java

创建 study2.java 文件,利用实现 Callable 进行返回,代码如下所示:

  1. import java.util.concurrent.Callable;
  2. public class study2 {
  3. public static void main(String[] args) {
  4. MyCallable MyCallable = new MyCallable("张方兴");
  5. String call = null;
  6. try {
  7. call = MyCallable.call();
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. System.out.println(call);
  12. }
  13. }
  14. class MyCallable implements Callable<String>{
  15. private String name;
  16. public MyCallable(String name) {
  17. this.name = name;
  18. }
  19. @Override
  20. public String call() throws Exception {
  21. return "call:" + name;
  22. }
  23. }

import java.util.concurrent.Callable; public class study2 { public static void main(String[] args) { MyCallable MyCallable = new MyCallable(“张方兴”); String call = null; try { call = MyCallable.call(); } catch (Exception e) { e.printStackTrace(); } System.out.println(call); } } class MyCallable implements Callable{ private String name; public MyCallable(String name) { this.name = name; } @Override public String call() throws Exception { return “call:” + name; } }

study2 运行结果

多进程和多线程的概念 - 图10

Callable 接口详解

一般继承 Thread 的类,含有 .start() 函数,所以直接可以使用 .start() 函数进行启动。
实现 Runnable 的类,需要通过 new Thread(myRunnable).start(); 的方式进行启动,即实现 Runnable 的类只是做好了一段多线程所需执行的内容,自身并没有执行的能力,需要通过 Thread 类的 .start() 函数进行启动。
实现 Callable 的接口,含有 .call() 函数,所以可以直接使用 .call() 函数进行启动,另外值得说明的是,Callable 函数具有返回值,返回值为定义类时使用的 类型,其定义是其返回。Callable 接口定义如下所示:

  1. @FunctionalInterface
  2. public interface Callable<V> {
  3. /**
  4. * Computes a result, or throws an exception if unable to do so.
  5. *
  6. * @return computed result
  7. * @throws Exception if unable to compute a result
  8. */
  9. V call() throws Exception;
  10. }

@FunctionalInterface public interface Callable { /* Computes a result, or throws an exception if unable to do so. @return computed result @throws Exception if unable to compute a result / V call() throws Exception; }
Callable 用于指示接口类型声明是由 Java 语言规范定义的功能接口。从概念上讲,函数接口只有一个抽象方法。因为 java.lang.reflect.Method#isDefault()default methods 有一个实现,所以它们不是抽象的。如果接口声明一个抽象方法重写 java.lang.Object 的一个公共方法,则该方法也不计入接口的抽象方法计数,因为接口的任何实现都将具有来自 java.lang.Object 或其他位置的实现。
另外注意,函数接口的实例可以使用 lambda 表达式、方法引用或构造函数引用创建。

  • Callable 在需要使用返回值的情况下,程序是同步运行的
  • Callable 其它情况下,程序是异步运行的

在阅读 Callable 接口的过程中,建议读者多做几个测试,查看相应返回、程序执行时间,看出异步与同步的区别,由于代码过于简单,只需要增加几个 sleep() 函数,在 study2.java 类中更改一下即可,所以这里略过不提。