一、基础概念
1、进程和线程
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
2、并发性和并行性
并行:是指同一时刻,有多条指令在多个处理器上同时执行
并发:是指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行。通常多个进程可以在当个处理器上并发执行。
3、多进程和多线程
多进程:指操作系统上同时运行多个任务
多线程:某个进程,并发处理多个任务(同一个程序中有多个程序流在执行)
二、线程的创建和启动
在java中要想实现多线程,有三种方式
1、继承Thread类(使用继承Thread类的方式来创建线程类时,多个线程之间无法共享线程类的实例变量)
package threadtest; public class FirstThread extends Thread { private String name; public FirstThread(String name) { this.name = name; } public void run() { for(int i=0; i<10; i++) { System.out.println(name + ": " +i + "****"+Thread.currentThread().getName()); } } public static void main(String[] args) { FirstThread t1 = new FirstThread("thread1"); FirstThread t2 = new FirstThread("thread2"); t1.start(); t2.start(); System.out.println(Thread.currentThread().getName()); } }
2、实现Runnable接口(使用实现Runnable接口的方式来创建线程类时,多个线程之间共享线程类的实例变量)
package threadtest; public class SecondThread implements Runnable{ private String name; public SecondThread(String name) { this.name = name; } int i=0; @Override public void run() { for(;i<10;i++) { System.out.println(name + ": "+i+"****"+Thread.currentThread().getName()); } } public static void main(String[] args) { SecondThread st = new SecondThread("secondThread"); Thread t1 = new Thread(st); Thread t2 = new Thread(st); Thread t3 = new Thread(st,"线程3");//可以指定线程名称 t1.start(); t2.start(); t3.start(); System.out.println(Thread.currentThread().getName()); } }
3、实现Callable接口和Future
用Callable接口创建启动多线程的步骤
(1)创建Callable接口实现类,并实现call()方法
(2)创建Callable实现类的实例,使用FutureTask类包装Callable对象
(3)使用FutureTask对象做为Thread对象的target创建并启动新的线程
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
package threadtest; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class ThirdThread implements Callable<Integer>{ private String name; public ThirdThread(String name) { this.name = name ; } int i=0; @Override public Integer call() throws Exception { for(;i<10;i++) { System.out.println(name + ": " + i +"****"+ Thread.currentThread().getName()); } return i; } public static void main(String[] args) { ThirdThread tt = new ThirdThread("thirdThread"); FutureTask<Integer> task1 = new FutureTask<>(tt); FutureTask<Integer> task2 = new FutureTask<>(tt); FutureTask<Integer> task3 = new FutureTask<>(tt); Thread t1 = new Thread(task1); Thread t2 = new Thread(task2); Thread t3 = new Thread(task3,"线程3"); t1.start(); t2.start(); t3.start(); System.out.println(Thread.currentThread().getName()); } }
三种方式对比
通过上面三种方式都可以实现多线程,其中实现Runnable接口和实现Callable接口的方式基本相同,只是Callable接口里的方法有返回值,可以声明抛出异常。所以这两种方式可以归为一类
实现Runnable接口和实现Callable接口的方式
优势:
(1)线程类只实现了接口,还可以集成其他类
(2)多个线程可以共享一个target对象,非常适合多个线程来处理同一份资源的情况。代码和数据独立。
劣势:
(1)编程稍微复杂
继承Thread类方式
优势:
(1)编写简单
劣势:
(1)已经继承了Thread类,不能继承其他类
开发中一般推荐采用Runnable接口、Callable接口的方式来实现多线程。