Java三种创建多线程方法简介

什么是线程

  1. 现代操作系统在运行一个程序时,会为其创建一个进程。但是现代操作系统调度的最小单元是线程
  2. 一个进程由多个线程组成,一个线程也可以创建线程。
  3. 线程拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。
  4. 处理器在这些线程上高速切换(时间片轮转法等调度算法),让使用者感觉到这些线程在同时执行。(Thread.sleep(1000),并不代表线程睡眠1000毫秒,有可能操作系统调度来不及执行此线程所以可能存在睡眠时间比1000毫秒多的情况)

什么是进程

  1. 进程是操作系统中运行的一个任务
  2. 进程中所包含的一个或多个执行单元成为线程。
  3. 进程拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。
  4. 线程只能归属于一个进程并且它只能访问该进程所拥有的资源。

进程与线程的区别

  1. 一个线程至少有一个线程,线程的划分尺度小于进程,使得多线程程序的并发度高。

为什么要使用多线程

  1. 更多的处理器核心
  2. 更快的响应时间
  3. 更好的编程模型

线程的使用场景

1.一个程序中需要同时完成多个任务
2. 使得多线程可以更快完成情况

并发的原理

多个线程“同时”运行只是感官上的一种表现。事实上是并发运行的。OS将时间划分为很多时间片段(时间片),尽可能均匀分配给每一个线程,获得时间片的线程会被CPU执行。而其他线程全部等待。但是时间片轮转的非常快。宏观上都运行,这种叫并发

线程状态

在这里插入图片描述

  1. NEW:初始状态,线程被构建,但是未调用start()方法。
  2. RUNABLE:运行状态,操作系统中的“就绪”,“运行”
  3. BLOCKED:线程因为某种原因放弃了cpu 使用权,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得时间片运行。阻塞的情况分三种:
    1)等待阻塞:线程调用object.wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的等待队列,如果当前的线程拥有锁会释放锁。(object.wait(),等待阻塞)
    2)同步阻塞:运行的线程在获取同步锁时,若该同步锁被别的线程占用,则会把该线程放入同步队列中**(锁竞争阻塞)**
    3)其他阻塞:线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,该线程为阻塞状态。(睡眠,IO阻塞)
  4. WAITING:等待状态,线程调用object.wait()线程进入等待状态,该线程需要等待其他线程做出通知或者中断做出相应操作
  5. TIME_WAITING:超时等待状态,不同于WAITING状态是在超过规定时间,还没有被通知或者中断则自行返回
  6. TERMINATED:线程已经执行完毕

创建线程的方式

  1. 继承Thread类
    启动线程是调用start()方法,run方法时线程要执行的任务。当线程调用start()线程则进入RUNING,一旦获得cpu时间,run方法就会被调用
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
        
        //或者内部匿名实现类
        new Thread(){
            @Override
            public void run() {
                while (true){
                    System.out.println("现在运行的线程是:"+Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("现在运行的线程是:"+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 实现Runnable接口并重写run方法
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread runnableThread = new Thread( new Runnable(){
            @Override
            public void run() {
                while (true){
                    System.out.println("现在运行的线程是:"+Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        runnableThread.start();
    }
}
  1. 实现Callable接口:可以得到线程执行的返回值,可以抛出异常
public class ThreadDemo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 将Callable封装到FutureTask
        // FutureTask也是一种Runnable(也实现了Runnable)
        FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
        new Thread(futureTask).start();
        // get方法会阻塞调用的线程
        System.out.println("线程返回的值"+ futureTask.get());
    }
}
class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"starting...");
        int sum = 0;
        for (int i = 0; i <= 100000; i++) {
            sum += i;
        }
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName()+ "over...");
        return sum;
    }
}

Callable接口接受一个泛型作为接口中call方法的返回值类型。

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

创建实现Callable接口的对象,将其注册到FutureTask类中,然后将FutureTask类的实例注册进入Thread中运行。最后调用FutureTask中的get方法获取线程的返回值。

public class FutureTask<V> implements RunnableFuture<V>{ 
     public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
}

这里值得注意的是,使用callable的流程。FutureTask实现了RunnableFuture接口,RunnableFuture又继承了Runnable接口(接口可以继承接口)。

Thread类

无论怎样实现多线程,都需要调用Thread类中的start方法去启动线程。
Tread有一个构造方式是传入Runnable类型的参数,而FutureTask实现了 RunnableFuture接口,而 RunnableFuture 接口又继承了Runnable接口和 Funture接口,因此我们可以将FutureTask 的一个实例当做是一个Runnable接口的实例传入Thread来启动线程。

Thread类Runnable接口Callable接口以及FutureTask的关系图

在这里插入图片描述

  1. 继承Thread类
    在这里插入图片描述
  2. 实现Runnable接口并重写run方法
    在这里插入图片描述
  3. 实现Callable接口
    在这里插入图片描述

守护线程

守护线程与前台线程在表现上没有区别。thread.setDaemon(true);即可开启守护线程。当进程里的所有前台进程结束时候,进程就会结束。即守护线程强制结束。GC垃圾回收即运行在守护线程上,当程序运行过程中会持续产生垃圾,当程序运行结束守护线程也运行完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值