JUC源码第一章--线程

JUC--指的是 java.util.concurrent 包

Runnable、Callable是定义执行任务的接口

Future定义了线程执行任务时扩展功能的一个接口

jdk封装好了一个FutureTask类来实现了Future和Runnable接口

一.接口源码

Future

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Callable

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Runnable

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

二.开启线程的三种方式

  1. 实现接口:

通过定义一个类实现Runnable接口,再将实现类作为参数创建一个Thread对象,通过调用Thread对象的strat()方法开启。

或者定义一个类实现Callbale接口,将实现类作为参数构建一个FutureTask对象,将FutureTask作为一个Runnable参数再创建Thread对象,调用start()方法开启

  1. 继承父类:

定义一个类继承Thread类,那这个类就相当于是一个线程类了,再通过重写run()方法,创建线程对象,调用start()方法开启新线程执行你重写的run()方法。

  1. 线程池:

创建一个线程池,通过线程池来创建和开启线程,线程池内容后续可参考这篇文章XXX。

优点和区别:

1.实现接口方式和继承父类方式,因为java是单继承模式,通过继承父类就没办法继承其他类了,对于后续重构代码会有一定的影响。并且继承类里面的属性资源是线程私有的,而接口上面定义的属性资源是资源共享的。

2.线程池可以更好管理多个线程的生命周期

3.线程池的应用场景:需要开启多个线程异步工作的时候

三.扩展:

  1. new Thread(futureTask1).start()和new Thread(futureTask1).run()的区别?

用一个测试程序作为入口来分析

@SpringBootTest
class SpringStudyApplicationTests {

    @Test
    void contextLoads() throws ExecutionException, InterruptedException {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        FutureTask<String> futureTask1 = new FutureTask<>(mt1);
        FutureTask<String> futureTask2 = new FutureTask<>(mt2);
//        new Thread(futureTask1).run();
//        new Thread(futureTask2).run();
        new Thread(futureTask1).start();
        new Thread(futureTask2).start();

        // 获取运算结果是同步过程,即 call 方法执行完成,才能获取结果
        String r1 = futureTask1.get();
        String r2 = futureTask2.get();

        System.out.println(r1);
        System.out.println(r2);

    }
    
    class MyThread implements Callable<String> {//实现Runnable也可以
        @Override
        public String call() throws Exception {
            System.out.println("call"+Thread.currentThread().getName());
            return "call"+Thread.currentThread().getName();
        }

    }
}
start()
new Thread(futureTask1).start();
new Thread(futureTask2).start();
测试结果:
callThread-2
callThread-1
callThread-1
callThread-2
run()
new Thread(futureTask1).run();
new Thread(futureTask2).run();
测试结果:
callmain
callmain
callmain
callmain
结论:
start()方法是开启了多线程(异步,可以通过结果1得出)调用了run()方法,run()仅仅只是主线程调用了它定义的方法。
分析源码:

可以看到start()方法里面调用了start0()方法,

而这个方法有native关键字,表示是jvm自定义的,不需要关注,我们只需要知道jvm去调度了资源,开启了一个新的线程来执行你的run()方法就好了

private native void start0();

public
class Thread implements Runnable {

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        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();

    /**
     * If this thread was constructed using a separate
     * {@code Runnable} run object, then that
     * {@code Runnable} object's {@code run} method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of {@code Thread} should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}
  1. 守护线程和本地线程(用户线程)的区别?

守护线程:一种服务型线程,专门为本地线程(用户线程)服务的。

本地线程:平时我们使用和新建的线程,就称为本地线程,也可以叫做用户线程,为用户服务的。

守护线程可以通过Thread.setDaemon()来配置。

区别:它们在JVM中的销毁时机不同,本地线程当执行完毕就会进行出栈销毁(终止状态),而守护线程会在所有的本地线程都销毁之后,无论是否还在工作,都会进行退出。

典型的守护线程就是:垃圾回收线程,当所有本地线程都结束之后,就不会产生垃圾对象了,垃圾回收线程就没有存在的意义了,所以它就会自动退出。

  1. 线程和进程的区别?

进程包含了线程,一个进程至少有一个线程。

程序计数器、虚拟机栈和本地方法栈对于线程是私有的,所以切换线程比进程负担要小。

进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。

  1. 什么是多线程的上下文切换?

多线程cpu通过分配时间片来进行不断的切换线程,而线程的切换会保留上一个线程的状态(包括cpu的寄存器和程序计数器在某一时间点的内容等,称为上下文信息),便于切换回来时可加载这个线程的状态,保证了一个安全性和资源的开销,而切换的过程保存线程状态和加载回线程状态就叫做上下文的切换。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值