线程的综述
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
与进程比较:
进程是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块PCB中。以表示该进程拥有这些资源或正在使用它们。另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量。通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。
线程与进程的区别可以归纳为以下4点:
- 地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
- 通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
- 调度和切换:线程上下文切换比进程上下文切换要快得多。
- 在多线程OS中,进程不是一个可执行的实体。
三种创建方式
- 一、继承Tread
/**
* 模拟龟兔赛跑,实现多线程方法1
* 1、创建多线程 继承Thread 需要重写run(线程体)方法 相当于程序从run()方法开始
* 2、使用线程:创建子类对象
* 3、调用对象.start方法,启动线程,自己会调用内部的run方法
* @author Administrator
*
*/
public class rabbit extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<100;i++){
System.out.println("兔子跑了"+i);
}
}
}
class Tortoise extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<100;i++){
System.out.println("乌龟跑了"+i);
}
}
}
public class Rabbitapp {
public static void main(String[] args) {
//创建子类对象
rabbit r = new rabbit();
Tortoise t = new Tortoise();
//调用start方法,启动线程,自己会调用内部的run方法
r.start();
t.start();
//r.run 如果这么使用的话就会先把一个线程执行完之后,才会去执行其他的
for(int i=0;i<100;i++){
System.out.println("main-->"+i);
}
}
}
输出结果:乌龟跑了0 乌龟跑了1 兔子跑了0 main-->0
兔子跑了1 乌龟跑了2 兔子跑了2 兔子跑了3
- 二、实现Runable接口实现多线程
/**
* 继承Tread类方式实现多线程有缺点:那就是如果我们的类已经继承了一个类,比如说该类已经继承了其他小类,
* 那么就无法继续继承Tread类
* 所以可以使用Runable接口实现多线程
* 优点:可以同时实现继承,避免单继承,方便共享资源,同一份资源,多个代理访问
* 静态代理 设计模式
* 1、真实角色
* 2、代理角色
* 3、二者实现相同的接口
* @author Administrator
*
*/
public class StaticProxy {
public static void main(String[] args) {
You you = new You(); //创建真实角色
WeddingCompany we = new WeddingCompany(you); //创建代理角色+真实角色的引用
//执行任务
we.marry();
}
}
//接口
interface Marry{
void marry();
}
//真实角色
class You implements Marry{
@Override
public void marry() {
System.out.println("结婚了");
}
}
//代理角色-->持有真实角色的引用
class WeddingCompany implements Marry{
private Marry you;
public WeddingCompany() {
}
public WeddingCompany(Marry you) {
super();
this.you = you;
}
private void before(){
System.out.println("准备新房");
}
private void after(){
System.out.println("入洞房");
}
@Override
public void marry() {
before();
you.marry();
after();
}
}
下面就利用runnable实现多继承:
public class Video {
public static void main(String[] args) {
//创建真实角色
ChatQQ c = new ChatQQ();
//创建代理角色
Thread t = new Thread(c);
//调用.stat,启动线程
t.start();
for(int i=0;i<20;i++){
System.out.println("在看"+i+"集电视剧!");
}
}
}
class ChatQQ implements Runnable{
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println("和"+i+"个人在聊QQ!");
}
}
}
public class Web12306 implements Runnable {
private int num = 50;//1到50号
@Override
public void run(){
while(true) {
if(num <= 0){
break;//跳出循环
}
System.out.println(Thread.currentThread().getName() + "抢到了" + num--);
}
}
public static void main(String[] args) {
//真实角色
Web12306 web = new Web12306();
//代理
Thread t1 = new Thread(web, "路人甲");
Thread t2 = new Thread(web, "黄牛乙");
Thread t3 = new Thread(web, "攻城狮");
t1.start();
t2.start();
t3.start();
}
}
- 三、实现Callable接口
优点:可以获取返回值
Callable和Future接口
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable接口的类都是可被其它线程执行的任务
Callable和Runnable有几点不同:
1、Callable规定的方法是call(),而Runnable规定的方法是run()
2、call()方法可抛出异常,而run()方法不能抛出异常
3、Callable的任务执行后可返回值,运行Callable任务可得到一个Future对象,而Runnable的任务不能返回值
Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
缺点:繁琐
思路:
1)创建:Callable实现类 +重写call
2)借助:执行调度服务ExecutorService,获取Future对象
ExecutorService ser = Executors.newFixedThreadPool(2);
Future result = ser.submit(实现类对象)
3)获取值:result.get()
4)停止服务:ser.shutdownNow();
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test_Callable {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程
ExecutorService ser = Executors.newFixedThreadPool(2);//2个线程
Race gui = new Race("乌龟",1000);
Race tutu = new Race("小兔子",100);
//获取值
Future<Integer> result1 = ser.submit(gui);
Future<Integer> result2 = ser.submit(tutu);
Thread.sleep(2000);
gui.setFlag(false);
tutu.setFlag(false);
int num1 = result1.get();
int num2 = result2.get();
System.out.println("乌龟跑了-->>" + num1 + "步");
System.out.println("小兔子跑了-->>" + num2 + "步");
//停止服务
ser.shutdownNow();
}
}
class Race implements Callable<Integer>{
private String name; //名称
private long time; //延时
private boolean flag=true;
private int step; //步数
public Race() {
}
public Race(String name) {
super();
this.name = name;
}
public Race(String name, long time) {
super();
this.name = name;
this.time = time;
}
public Race(String name, long time, boolean flag, int step) {
super();
this.name = name;
this.time = time;
this.flag = flag;
this.step = step;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
@Override
public Integer call() throws Exception {
while(flag){
Thread.sleep(time);
step++;
}
return step;
}
}