多线程
来源:互联网 发布:mysql查看用户表空间 编辑:程序博客网 时间:2024/04/29 13:05
- 概述
- 1 进程
- 2线程
- 3多线程的优势
- 线程的创建和启动
- 1 继承Thread类创建线程类
- 2 实现Runnable创建线程类
- 3 使用Callable和Future创建线程
- 创建线程的三种方式对比
- 线程的生命周期
1.概述
1.1 进程
进程的特征:
(1)独立性:进程是系统中独立存在的实体,他可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间
(2)动态性:程序只是一个静态的指令集合,进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。
(3)并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会相互影响。
并行:指在同一时刻,有多条指令在多个处理器上同时执行;
并发:指在同一时刻,只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
1.2线程
(1)线程是进程的执行单元,线程在程序中是独立的、并发的执行流。
(2)当进程被初始化后,主线程就被创建了。
(3)线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程的调度和管理由金城本身负责完成。
(4)线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源,他与父进程的其他线程共享该进程所拥有的全部资源。
(5)一个线程可以创建和撤销另一个进程,同一个进程中的多个线程之间可以并发执行。
1.3多线程的优势
与分割的进程相比,进程中线程之间的隔离程度要小。他们共享内存、文件句柄和其他每个进程应有的状态。进程在执行过程中拥有独立的内存单元,而多个线程共享内存。
线程比进程具有更高的性能,因为**多个线程共享同一个进程的虚拟空间。**线程的共享还包括:**进程代码段**、**进程的共有数据等**。利用这些共享的数据,线程很容易实现相互之间的通信。
多线程编程的优点:
(1)进程之间不能共享内存,但线程之间共享内存非常容易;
(2)系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高。
(3)Java语言内置了多线程功能支持,而不是淡出地作为底层操作系统的调度方式,从而化简了Java的多线程编程。
2.线程的创建和启动
Java使用Thread类代表线程,所有的线程都必须是Thread类或其子类的实例。下面是三种创建线程类的方式。
2.1 继承Thread类创建线程类
步骤:
1.定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此吧run()方法称为线程执行体。
2.创建Thread子类的实例,即创建了线程对象。
3.调用线程对象的start()方法来启动该线程。
主线程的线程执行体不是由run()方法确定的,而是由main()方法确定的——main()方法的方法体代表主线程的线程执行体。
注意:使用继承Thread类的方法来创建线程时,多个线程之间无法共享线程类的实例变量。
Eg:
public class FirstThread extends Thread{ private int i; public void run(){ for ( ; i < 100; i++) { //当线程类集成Thread类时,直接使用this即可获得当前线程 System.out.println(getName()+" "+i); } } public static void main(String[] args) { // TODO Auto-generated method stub for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+" "+i); if (i == 20) { //创建并启动线程 new FirstThread().start(); new FirstThread().start(); } } }}
2.2 实现Runnable创建线程类
步骤:
1.定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
3.调用线程对象的start()方法来启动线程。
Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。
public class SecondThread implements Runnable { private int i; @Override public void run() { // TODO Auto-generated method stub for (; i < 100; i++) { //当线程类实现Runnable接口时,如果想获取当前线程,只能用Thread.currentThread()方法 System.out.println(Thread.currentThread().getName()+" "+i); } } public static void main(String[] args) { // TODO Auto-generated method stub for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+" "+i); if (i == 20) { SecondThread st = new SecondThread(); new Thread(st,"新线程1").start(); new Thread(st,"新线程2").start(); } } }}
采用Runnable接口的方式创建的多个线程可以共享线程类的实例变量。因为在这种方式下,程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target,所以多个线程可以共享同一个线程类(实际上应该是现成的target类)的实例变量。
Runnable接口中只包含一个抽象方法,从Java8开始,Runnable接口使用了@FunctionalInterface修饰。也就是说,Runnable接口是函数式接口,可使用Lambda表达式创建Runnable对象。
2.3 使用Callable和Future创建线程
Callable接口提供了一个call()方法可以作为线程执行体,call()方法可以有返回值,且可以声明抛出异常。
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现了Future接口,并实现了Runnable接口——可以作为Thread类的target。
Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法返回值类型相同。而且Callble接口是函数式接口,因此可使用Lambda表达式创建Callable对象。
步骤:
1.创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,再创建Callable实现类的实例。从Java8开始,可以直接使用Lambda表达式创建Callable对象。
2.使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
3.使用FutureTask对象作为Thread对象的target创建并启动新的线程。
4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
Eg.
import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;public class ThirdThred { public static void main(String[] args) { // TODO Auto-generated method stub //创建Callable对象 ThirdThred rt = new ThirdThred(); //先使用Lambda表达式创建Callable<Integer>对象 //使用FutureTask来包装Callable对象 //FutureTask ft = new FutureTask<>(callable); FutureTask task = new FutureTask<Integer>((Callable<Integer>) () -> { int i = 0 ; for (; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"的i="+i); } //call()可以有返回值 return i; }); for (int i=0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"的i="+i); if(i==20){ //实质上还是以Callable对象来创建和启动线程的 new Thread(task,"有返回值的线程").start(); } } try{ //获取返回值 System.out.println("子线程的返回值:"+task.get()); }catch (Exception e) { // TODO: handle exception e.printStackTrace(); } }}
3 创建线程的三种方式对比
- 实现Runnable、Callable接口的方式创建多线程的优缺点:
1.线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
2.在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
3.劣势是,编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法。
- 继承Thread类的方式创建多线程的优缺点:
1.编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接this即可获得当前线程。
2.劣势是,因为线程类已经继承了Thread类,所以不能再继承其他父类。
4.线程的生命周期
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- Mesos1.0.0的革新(转)
- 杀死系统中的进程kill和killall命令
- Spring事务管理
- Android Studio修改快捷键
- AVL树 插入节点和删除节点 简单策略
- 多线程
- C#中的combobox里DropDownStyle
- ArrayList源码解析
- 商品系统设计(一):商品主表设计
- On teaching mathematics--Arnold
- 实现一个模块module用来进行字符串的逆置
- JavaScript获取时间,处理时间
- 欢迎使用CSDN-markdown编辑器
- Cygwin运行Lighttpd + FCGI + PHP