一、概念
1.进程与线程的区别
进程
- 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在 指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的 。
- 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
- 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)。
线程
- 一个进程之内可以分为一到多个线程。
- 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU 执行
- Java 中,线程作为最小调度单位,进程作为资源分配的最小单位。 在 windows 中进程是不活动的,只是作 为线程的容器
二者对比
- 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
- 进程拥有共享的资源,如内存空间等,供其内部的线程共享
- 进程间通信较为复杂
- 同一台计算机的进程通信称为 IPC(Inter-process communication)
- 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
- 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
-
2.并行与并发
并发::在同一时刻,有多个指令在单个 CPU 上交替执行

- 并行:在同一时刻,有多个指令在多个 CPU 上同时执行

3.同步和异步
同步异步:
- 需要等待结果返回,才能继续运行就是同步
- 不需要等待结果返回,就能继续运行就是异步
二、使用线程
未开启线程并发
public class MyThread {public static void main(String[] args) {BranchThread t=new BranchThread();t.run();for(int i=0;i<=100;i++){System.out.println("主线程"+i);}}}class BranchThread extends Thread{public void run(){for(int i=0;i<=1000;i++){System.out.println("分支线程------------->"+i);}}}
1: 自上而下的顺序
2:先输出分支线程1-1000,输出完之后再输出
主线程1-100
1.继承Thread
实现线程的第一种方式:
编写一个类,直接继承java.lang.Thread,重写run方法。
public class MyThread {public static void main(String[] args) {BranchThread t=new BranchThread();//start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。t.start();for(int i=0;i<=1000;i++){System.out.println("主线程"+i);}}}class BranchThread extends Thread{public void run(){for(int i=0;i<=1000;i++){System.out.println("分支线程------------->"+i);}}}

"C:\Program Files\Java\jdk-15.0.1\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.3.1\lib\idea_rt.jar=64875:C:\Program Files\JetBrains\IntelliJ IDEA 2020.3.1\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\cao\IdeaProjects\untitled3\out\production\untitled Thread.ThreadTest02分支线程--------->0主线程0分支线程--------->1主线程1分支线程--------->2分支线程--------->3主线程2分支线程--------->4主线程3分支线程--------->5主线程4分支线程--------->6主线程5分支线程--------->7主线程6分支线程--------->8主线程7分支线程--------->9主线程8分支线程--------->10主线程9分支线程--------->11主线程10分支线程--------->12主线程11主线程12分支线程--------->13主线程13分支线程--------->14主线程14分支线程--------->15
2.实现Runnable接口
实现线程的第二种方式,编写一个类实现java.lang.Runnable接口。
public class MyThread {public static void main(String[] args) {/*** 创建一个可运行的对象* BranchThread r= new BranchThread();* 将可运行的对象封装成一个线程对象* Thread t=new Thread(r)*/Thread t = new Thread(new BranchThread());//合并代码t.start();for(int i=0;i<=100;i++){System.out.println("主线程"+i);}}}class BranchThread implements Runnable{public void run(){for(int i=0;i<=100;i++){System.out.println("分支线程------------->"+i);}}}
采用匿名内部类的方式【第二种变形】
public class MyThread {public static void main(String[] args) {//创建线程对象,采用匿名内部类方式。Thread t = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {System.out.println("t线程----->" + i);}}});t.start();for (int i = 0; i <= 100; i++) {System.out.println("main线程----->" + i);}}}
3.实现Callable接口
实现线程的第三种方式:
实现Callable接口
这种方式的优点:可以获取到线程的执行结果。
这种方式的缺点:效率比较低,在获取t线程执行结果的时候,当前线程受阻塞,效率较低。
import java.util.concurrent.Callable;import java.util.concurrent.FutureTask; // JUC包下的,属于java的并发包,老JDK中没有这个包。新特性。public class MyThread {public static void main(String[] args) throws Exception {// 第一步:创建一个“未来任务类”对象。// 参数非常重要,需要给一个Callable接口实现类对象。FutureTask task = new FutureTask(new Callable() {@Overridepublic Object call() throws Exception { // call()方法就相当于run方法。只不过这个有返回值// 线程执行一个任务,执行之后可能会有一个执行结果// 模拟执行System.out.println("call method begin");Thread.sleep(1000 * 10);System.out.println("call method end!");int a = 100;int b = 200;return a + b; //自动装箱(300结果变成Integer)}});// 创建线程对象Thread t = new Thread(task);// 启动线程t.start();// 这里是main方法,这是在主线程中。// 在主线程中,怎么获取t线程的返回结果?// get()方法的执行会导致“当前线程阻塞”Object obj = task.get();System.out.println("线程执行结果:" + obj);// main方法这里的程序要想执行必须等待get()方法的结束// 而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果// 另一个线程执行是需要时间的。System.out.println("hello world!");}}
call method begin/*等待sleep */call method end!/*等待另一个线程结束拿到300*/线程执行结果:300hello world!

