zoukankan      html  css  js  c++  java
  • 面试题---->线程的入门,读完可以应付一般的面试(管理员不要移除我的随笔啊)

    这个都是入门和一般的常规知识,大佬轻喷

    ①、继承Thread类

    ②、实现Runnable接口(常用,优点多)

    ③、实现Callable接口

    实现Runnable和Callable接口的类只能当作一个可以在线程中运行的target,不是真正意义上的线程,最后还需要Thread来调用。

    在Runnable中,实现类的run方法作为线程的执行体,实际的线程对象依然是Thread实例。Callable接口与Runnable相比,Callable可以有返回值,返回值通过FutureTask进行封装,Callable还可以向上抛出异常。

    ————————————————

    一、继承Tread类

    Thread类实现了Runnable接口,在java.long包下。

    创建执行线程方法一:将类继承Thread类,重写Thread类的run方法。接下来就可以分配并启动该子类的实例。

    具体步骤:

    ①、继承Thread类

    ②、重写run方法

    ③、将执行的代码写在run方法中

    ④、创建Thread类的子类对象

    ⑤、使用start方法开启线程。

    注意:调用run方法不能开启多线程。

    只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。

    如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。

    一个线程不能多次开启是非法的

    下面就按照上述继承Tread类实现多线程的例子

    package com.bjut.Multithreading;

    public class ThreadTest {

        public static void main(String[] args) {

            //4,创建Thread类的子类对象

            MyThread mt = new MyThread();

            //5,使用start方法开启线程

            mt.start();

            for (int i = 0; i < 5; i++) {

                System.out.println("main方法:" + i);

            }

        }

    }

    //1.继承Thread类

    class  MyThread extends  Thread{

        //2,重写run方法

        @Override

        public void run(){

            //3,将执行的代码写在run方法中

            for (int i = 0; i <5; i++) {

                System.out.println("MyThread的run方法:"+i);

            }

        }

    }

    只有调用了start方法才是启动了多线程,输出就是固定为mainrun(因为run方法中有休眠的时刻,多线程main语句就已经执行了)

    方法二:实现Runnable接口

    声明实现Runnable接口的类,实现Runnable接口中仅有的run方法,然后分配实例对象,在创建Thread时作为一个参数来传递并启动。具体步骤如下

    ①、定义类实现Runnable接口

    ②、在该类中实现Runnable接口中的run()方法

    ③、线程中具体要执行的东西写在run()方法中

    ④、创建自定义Runnable实现类对象

    ⑤、创建Thread类的对象,并在该对象中传入该实现Runnable接口的对象作参数

    ⑥、Thread类的对象调用start()方法开启新线程,其内部会自动的调用run方法

    package com.bjut.Multithreading;

    public class RunnableTest {

        public static void main(String[] args) {

            MyRunnable mr = new MyRunnable(); //④、创建自己定义的Runnable实现类的对象

            Thread thread = new Thread(mr); //⑤、创建Thread类的对象,并将自定义Runnable实现类的对象作为参数传递给Thread的构造函数

            thread.start(); //⑥使用thread类的start方法开启线程。

            for (int i = 0; i < 5; i++) {

                System.out.println("main方法中:" + i);

            }

        }

    }

         //①、定义一个Runnable实现类

    class MyRunnable implements Runnable {

        //②、实现Runnable接口中的抽象方法

        @Override

        public void run() {

            //③、在run方法中写入要使用多线程的具体方法

            for (int i = 0; i < 5; i++) {

                System.out.println("MyRunnable的方法中" + i);

            }

        }

    }

    使用匿名内部类的方式来实现继承Thread类和实现Runnable接口的方法来实现多线程

    package com.bjut.Multithreading;

    public class AnonymousThread {

        public static void main(String[] args) {

            // 继承Thread类实现多线程

            new Thread() {

                @Override

                public void run() {

                    for (int i = 0; i < 5; i++) {

                        System.out.println("继承Thread类实现多线程"+Thread.currentThread().getName() + "--" + i);

                    }

                }

            }.start();

            // 实现Runnable接口实现多线程

            new Thread(new Runnable() {

                @Override

                public void run() {

                    for (int i = 0; i < 5; i++) {

                        System.out.println("实现Runnable接口实现多线程"+Thread.currentThread().getName() + "--" + i);

                    }

                }

            }) {

            }.start();

        }

    }

    多线程的两种实现方式(实现Runnable接口、和继承Thread类)的区别

    源码中的区别

    继承Thread类方式:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法(Java虚拟机自动完成)

    实现Runnable方式:构造函数中传入了Runnable的引用,传给了Thread类中的成员变量,start()调用了run()方法时的内部判断成员变量Runnable的引用是否为空,若不为空,编译时看的是Runnable的run(),运行时执行的是具体实现类中的run()

    优缺点:

    继承Thread类方式

    好处:可以直接使用Thread类中的方法,代码简单

    弊端:同样也是面向对象中的继承的缺点:如果该具体类已经有了其他的父类,那么就不能多重继承Thread类,就不能使用这种方法。此时面向接口编程的优势脱颖而出。

    实现Runnable接口方式

    好处:即继承的弊端:即使自己定义的线程类有了其他父类也可以实现该Runnable接口。Java中的接口是多实现的,继承是单继承,比较有局限性。

    弊端:不能直接使用Thread类中的方法,需要先把Runnable具体实现类对象传递给Thread类并获取到线程对象后,才能得到Thread类的方法,代码相对复杂

    方式三:实现Callable接口

    步骤:

    创建实体类,实现Callable接口

    实现接口中的call()方法

    利用 ExecutorService线程池对象 的 <T> Future<T> submit(Callable<T> task()方法提交该Callable接口的线程任务。

    实现callable接口,提交给ExecutorService返回值是异步执行的。

    该方式的优缺点:

    优点:

    有返回值

    可以抛出异常

    缺点:

    代码较复杂,需要利用线程池

    /**

     * @ Author zhangsf

     * @CreateTime 2019/9/21 - 8:15 AM

     */

    package com.bjut.Multithreading;

    import java.util.concurrent.Callable;

    import java.util.concurrent.ExecutionException;

    import java.util.concurrent.FutureTask;

    /**

     * Callable是一个接口,里面只有一个方法:call,此方法有返回值。

     */

    public class CallableDemo implements Callable {

        @Override

        public String call() throws Exception {    //可以有返回值

            return Thread.currentThread().getName();

        }

        public static void main(String[] args) throws InterruptedException, ExecutionException {

            CallableDemo cd=new CallableDemo();

            FutureTask ft=new FutureTask(cd);

            FutureTask ft1=new FutureTask(cd);

            Thread td=new Thread(ft);

            Thread td1=new Thread(ft1);

            td.setName("张三");

            td1.setName("李四");

            td.start();

            td1.start();

            System.out.println(ft.get());    //通过get方法获取返回值

            System.out.println(ft1.get());

        }

    }

                  使用线程池的方式:

    //1、corePoolSize 线程池核心线程大小
    //2、maximumPoolSize 线程池最大线程数量
    //3、keepAliveTime 空闲线程存活时间
    //4、unit 空闲线程存活时间单位
    //5.workQueue 工作队列
    /*①ArrayBlockingQueue
    基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。
    当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,
    等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
    ②LinkedBlockingQuene
    基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,
    当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,
    因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
    ③SynchronousQuene
    一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,
    不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,
    则执行拒绝策略。
    ④PriorityBlockingQueue
    具有优先级的无界阻塞队列,优先级通过参数Comparator实现。*/
    //6.threadFactory 线程工厂
    //7.handler 拒绝策略
    /*①CallerRunsPolicy
    该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
    ②AbortPolicy
    该策略下,直接丢弃任务,并抛出RejectedExecutionException异常
    ③DiscardPolicy
    该策略下,直接丢弃任务,什么都不做。
    ④DiscardOldestPolicy
    该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列*/
    ExecutorService executorService1 = new ThreadPoolExecutor(2,5,
            5, TimeUnit.SECONDS,new LinkedBlockingDeque<>(3),new ThreadPoolExecutor.AbortPolicy());

    创建规定大小的线程:

    ExecutorService executorService = Executors.newFixedThreadPool(5);
    单个线程
    ExecutorService executorService1 = Executors.newSingleThreadExecutor();
    可以扩容额线程
    ExecutorService executorService2 = Executors.newCachedThreadPool();

    在实际的开发中我们是不会使用java给我们提供的线程池的

    就是因为这个: BlockingQueue<Runnable> workQueue

    无界阻塞队列:

    他的值是int的最大值: this(Integer.MAX_VALUE);这样一来很容易就oom了.

    这个不是我瞎说的哦,java的底层代码就是这样实现的

    ThreadPoolExecutor

    一般就用的是这个创建线程池啦,7大参数,上面都有说明哦,

    希望对你们有帮助

  • 相关阅读:
    如何在iTerm2中配置oh my zsh?
    sublime中格式化jsx文件
    ES6 new syntax of Literal
    ES6 new syntax of Rest and Spread Operators
    How to preview html file in our browser at sublime text?
    ES6 new syntax of Default Function Parameters
    ES6 new syntax of Arrow Function
    七牛云2018春招笔试题
    Spring-使用注解开发(十二)
    Spring-声明式事物(十一)
  • 原文地址:https://www.cnblogs.com/niCong/p/14770360.html
Copyright © 2011-2022 走看看