线程概述
- 进程 线程
- 进程 线程概念
1、进程:操作系统 调度程序 动态概念
2、在进程内多条执行路径
.一个进程可拥有多个并行的线程
.一个进程中的线程共享相同的内存单元/内存地址空间、可以访问相同的变量和对象,而且它们从同一堆中分配对象、通信、数据交换、同步操作
.由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就是得通信更简便而且信息传递的速度也更快。
(多个线程执行一个结果时就会出现并发情况)
- 线程和进程的区别
区别 | 进程 | 线程 |
根本区别 | 作为资源分配的单位 | 调度和执行的单位 |
开销 | 每个进程都有独立的代码和数据空间,进程间的切换会有较大的开销。 | 线程可以看成轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(pc),线程切换的开销小。 |
所处环境 | 在操作系统中能同时运行多个任务(程序) | 在同一应用程序中有多个顺序流同时执行 |
分配内存 | 系统在运行的时候会为每个进程分配不同的内存区域 | 除了CPU之外,不会为线程分配内存 |
包含关系 | 没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个线程,则执行过程不是一条线的,而是多条线程共同完成的。 | 线程是进程的一部分,所以线程有的时候被称为是轻量级进程 |
- 了解完多线程之后先弄懂这两种发法区别
start()和run()的区别?
start():
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法代码执行完直接继续执行下面的代码。通过调用Thread
类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一但得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run()方法运行结束,此线程随即终止。
run():
run()方法只是类的一个普通方法而已,如果直接调用run()方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run()方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:
调用start()方法可启动线程,而run()方法只是Thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
一、线程创建之继承Thread类
package com.aozhi.thread;
/**
*这种方式的缺点:那就是如果我们的类已经从一个类继承,则无法再继承Thread类。
* 模拟赛车比赛
* 创建多线程继承thread类实现run()方法
* @author songhaibo
*
* 2019年7月11日-下午3:05:54
*/
public class TestThread {
public static void main(String[] args) {
Racing rc = new Racing();
Ferrari fa = new Ferrari();
rc.start();
fa.start();
//rc.run();
//fa.run();
for (int i = 0; i < 50; i++) {
System.out.println("main--->" + i);
}
}
}
class Racing extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("GTR跑了" + i + "英里");
}
}
}
class Ferrari extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("Ferrari跑了" + i + "英里");
}
}
}
二、线程创建之一实现Runnable接口
package com.aozhi.thread;
/**
* 使用静态代理模式
* 实现接口便于共享资源
* @author songhaibo
*
* 2019年7月11日-下午5:01:17
*/
public class TestRunnable {
public static void main(String[] args) {
//Runnable实现类对象,真实角色
Thread1 role1 = new Thread1();
//线程类代理角色,该类也实现了Runnable接口,代理角色
Thread thread1 = new Thread(role1);//传入了真实角色的引用
thread1.start();
}
}
class Thread1 implements Runnable {
@Override
public void run() {
//TODO
}
}
- 在这里实现Runnable接口用到了设计模式里的静态代理模式,首先我们来看一下静态代理模式
参考文章:https://www.cnblogs.com/yxiaooutlook/p/7798563.html
package com.aozhi.thread;
/**
* 静态代理模式
* 要求:真实角色,代理角色;真实角色和代理角色要实现同一个接口,代理角色要持有真实角色的引用。
*
*在Java中线程的设计就使用了静态代理设计模式,其中自定义线程类实现Runable接口,
*Thread类也实现了Runalbe接口,在创建子线程的时候,传入了自定义线程类的引用,
*再通过调用start()方法,调用自定义线程对象的run()方法。实现了线程的并发执行。
*
* @author songhaibo
*
* 2019年7月11日-下午4:17:22
*/
public class Staticproxy {
public static void main(String[] args) {
//创建真实角色对象
Proxy realityRole = new RealityRole();
//创建代理角色对象,并制定真实对象
ProxyRole proxyRole = new ProxyRole(realityRole);
//代理角色工作,本质调用的还是真实角色的功能
proxyRole.run();
}
}
/*
* 共同的接口
*/
interface Proxy {
public abstract void run();
}
//真实角色
class RealityRole implements Proxy {
@Override
public void run() {
System.out.println("run");
}
}
//
class ProxyRole implements Proxy {
// 持有代理角色的引用
private Proxy realityRole;
//传入一个真实角色
public ProxyRole(Proxy role) {
realityRole = role;
}
@Override
public void run() {
//在真实角色功能运行之前,代理角色做准备工作
doBefore();
//执行真实角色的功能
realityRole.run();
//代理角色的收尾工作
doAfter();
}
private void doBefore() {
System.out.println("准备工作");
}
private void doAfter() {
System.out.println("收尾工作");
}
}
三、实现Callable接口
这种方式主要就是针对前两种都是实现run()方法,但是run()方法没有返回值,这种方式就是可以抛异常有返回值。