Java多线程

Java多线程

首先我们先理解一下几个概念
程序,进程,线程,并发,并行
程序:计算机里面的代码,是静止不动的,例如我们电脑里所安装的一些程序。
进程:进程是程序的执行过程,是动态的,当程序运行时就在计算机里面创建了一个进程。进程需要使用计算机资源,cpu和内存。例如我们打开某个计算机程序,会在任务管理器中显示某个进程。
线程:线程是轻量级进程,是执行任务的一部分。一个进程可以有多个线程。例如我们运行qq,那么qq就是进程,而聊天窗口是一个线程。

并发:对于单核机器来说,多个线程交替执行。在宏观上是一块执行,但在微观上是处理器交替执行每个线程。
并行:对于单核机器来说,并行就是多台机器运行多个进程,互相之间是平行的。

Java程序中的多线程
Java支持多线程的 , 每次运行 java 程序都会启动多个线程 , 不管是那个 java 程序 ; java程序启动时 , 会开启 main 线程 , main方法就是main线程的执行体 , main方法结束 , main线程执行体完毕 , 自然就会结束 ;
守护线程 : 一般执行体不会结束 , 如果剩余的全是守护线程 , 那么他们会结束自己 ;

Java线程的几种状态

我们通过查看源码Thread.State查看

1.NEW
2.RUNNABLE
3.BLOCKED:阻塞
4.WAITING:
5.TIMED_WAITING:线程在等待唤醒,但设置了时限
6.TERMINATED

线程的创建方法

一、继承Thread类
1.定义一个类,继承java.lang.Thread()类;
2.重写run()方法,该方法是线程的执行体;
3. 在正在执行的线程中 , 创建子类对象-- 创建线程对象
4. 开启线程 , 调用 strat 方法 , 该方法可以沟通操作系统

public class TestThread {
    public static void main(String[] args) {

        Thread t1 = new Threads();
        //开启t1线程
        t1.start();

}

class Threads extends Thread{
    @Override
    public void run() {
        for(int i = 0; i < 20; i++){
            System.out.println("线程"+i);
            try{
                sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

当把 开启线程 strat 方法替换为 run 方法时 , 此时就是最普通的方法调用
在这里插入图片描述
通过start()源码我们发现有一个本地方法在沟通操作系统
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();

/* Notify the group that this thread is about to be started
 * so that it can be added to the group's list of threads
 * and the group's unstarted count can be decremented. */
group.add(this);

boolean started = false;
try {
    start0();
    started = true;
} finally {
    try {
        if (!started) {
            group.threadStartFailed(this);
        }
    } catch (Throwable ignore) {
        /* do nothing. If start0 threw a Throwable then
          it will be passed up the call stack */
    }
}

}

//沟通操作系统的方法
private native void start0();

既然有线程类 , 为何要继承线程类 , 还要重写 run 方法呢 ?
如下所示 Thread 类中的 run 方法 , 没有具体的执行内容故 , 要有Thread类的子类 , 还要重写覆盖 run 方法
@Override
public void run() {
if (target != null) {
target.run();
}
}

总结 : 此种方式创建开启线程最为简单 , 4个步骤即可 , 但是局限最大 --> Java 严格遵守单继承

二、第二种开启线程的方式 - 实现Runnable 接口

  1. 定义一个类 , 实现 Runnable 接口 , Thread也实现这个接口 run方法是这个接口定义的约束
  2. 重写实现 run 方法 , 该 run 方法就是线程的 执行体
  3. 在测试类中,构建 Runnable接口的实现类对象
  4. 套接到 Thread 对象中 Thread 可以接收 Runnable对象来构建自己 作为 target 属性存在
  5. 得到 Thread 对象之后 , start开启 , Thread对象的执行体是 run
  6. 在这里插入代码片

public class TestThread {
    public static void main(String[] args) {

        Thread2 thread2 = new Thread2();
        Thread t2 = new Thread(thread2);
        t2.start();
}

class Thread2 extends Thread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10 ; i++) {
            System.out.println("线程二:"+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

此方式总结 : 步骤繁琐属于中等 , 不需要继承 , 只需要实现即可 ;此种方式更适用于需要共享数据的场景 ;这是使用最多的开启线程的方式 ;

三、创建Callable 接口的实现类
1.创建Callable 接口的实现类
2.实现类 实现 call() 方法,该 call() 方法将作为线程执行体,且该 call() 方法有返回值 。
3.在正在运行的线程中 创建Callable 实现类的实例
4.使用 FutureTask 类来包装 Callable 子类对象, 该 FutrueTask 对象封装了该 Callable 对象的 call() 方法的返回值。
5.使用 FutureTask 对象作为 Thread 对象的 target属性 来构建对象
6.调用FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

package com.hive.udf;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class TestThread {
    public static void main(String[] args) {

    //3.Callable的实现类
        Callable call = new Thread3();
        // 4. 把 call 包装成 FutureTask 类对象
        // FutureTask->还是 Runnable的实现类
        FutureTask<Integer> task = new FutureTask<Integer>(call);
        // 5. 把 FutureTask (Runnable) 封装成 Thread 对象
        Thread t3 = new Thread(task);
        //6.开启
        t3.start();
	System.out.println(task.isDone()); // 查看任务是否结束
  	// 7 . 获取返回值 (可选操作)
  	Integer result = task.get(); // 阻塞方法 , 如果没结束那么就等
  	System.out.println("返回值是 : "+result);
  }
}
class Thread3 implements Callable<Integer>{
    //2.重写call方法
    @Override
    public Integer call() throws Exception {

        int result = 0;
        for (int i = 0; i < 10; i++) {
            result += i;
            System.out.println(i);
            Thread.sleep(100);
        }
        return result;
    }
}

Callable接口
Callable 位于java.util.concurrent包下,是个泛型接口,它提供了一个call() 方法可以作为线程执行体。call() 方法可以有返回值。call() 方法可以声明抛出异常。

Future接口
这是一个泛型接口,call()函数返回的类型就是传递进来的V类型Future接口Future就是对于具体的 Runnable 或者 Callable 任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过 get 方法获取执行结果,该方法会阻塞直到任务返回结果。

Future 接口中主要方法的作用:V get()
方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
V get(long timeout, TimeUnit unit)
用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
boolean isDone() 如果任务已完成,则返回 true。

FutureTask类Future 接口的实现类是 FutureTask,FutrueTask 实现类提供了两个构造方法:FutureTask(Callable callable) 创建一个 FutureTask,一旦运行就执行给定的 Callable。 FutureTask(Runnable runnable, V result) 创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。

此方式总结 : 1. 抛异常2. 可以有返回值3. 可以查询正在执行的线程是否结束 , 也可以获取返回值4. 操作步骤繁琐5. 共享数据6. FutureTask 只能开启一次 , Callable 对象可以套接到 FutureTask 上 ;

三种方法总结
三种方式的特性总结
复杂度 Thread - Runnable - Callable 依次递增
共享数据难度 Thread - Callable - Runnable 依次递减
使用量 Thread - Callable - Runnable 依次递增
需返回值和抛异常时 : 选择 Callable
无需返回值和抛异常时 : 优先选择 Runnable

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值