0
点赞
收藏
分享

微信扫一扫

java面试系列--java中线程的实现方式

目标践行者 2023-07-20 阅读 76

Java多线程实现的方式有四种

1 继承Thread类,重写run方法

2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target


3.通过Callable和FutureTask创建线程

4.通过线程池创建线程


具体实现

前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果 后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中

线程实现方式1:继承Thread类的线程实现方式如下:

1.继承Thread类,重写run方法 2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target 3.通过Callable和FutureTask创建线程(future.get()接口会阻塞); 4.通过线程池创建线程

前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果 后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中


方式1:继承Thread类的线程实现方式如下:
package org.example.myThread;
public class ThreadDemo01 extends Thread {
public ThreadDemo01() {
//编写子类的构造方法,可缺省
}

public void run() {
    //编写自己的线程代码
    for (int i = 0; i < 3; i++) {
        System.out.println(Thread.currentThread().getName() + " " + i);
    }
}

public static void main(String[] args) {

    ThreadDemo01 thread01 = new ThreadDemo01();
    ThreadDemo01 thread02 = new ThreadDemo01();
    thread01.setName("自定义的线程1");
    thread01.start();
    thread02.setName("自定义的线程2");
    thread02.start();

    //main线程
    System.out.println(Thread.currentThread().toString());
}
    
自定义的线程2 0
自定义的线程2 1
自定义的线程2 2
Thread[main,5,main]
自定义的线程1 0
自定义的线程1 1
自定义的线程1 2

}

线程实现方式2:通过实现Runnable接口,实现run方法,接口的实现类的实例作为Thread的target作为参数传入带参的Thread构造函数,通过调用start()方法启动线程

package org.example.myThread;

class MyThread implements Runnable {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        System.out.println(Thread.currentThread().getName() + "-->我是通过实现接口的线程实现方式!");
    }
}

public class ThreadDemo02 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread());
        t1.start();
        //thread 2
        new Thread(new MyThread()).start();
        //main线程
        System.out.println(Thread.currentThread().toString());
    }
}
Thread-0 0
Thread-0 1
Thread-0-->我是通过实现接口的线程实现方式!
Thread[main,5,main]
Thread-1 0
Thread-1 1
Thread-1-->我是通过实现接口的线程实现方式!



线程实现方式3:通过Callable和FutureTask创建线程

a:创建Callable接口的实现类 ,并实现Call方法

b:创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值

c:使用FutureTask对象作为Thread对象的target创建并启动线程

d:调用FutureTask对象的get()来获取子线程执行结束的返回值


package org.example.myThread;

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

class Tickets<Object> implements Callable<Integer> {
    private int i;

    public Tickets(int i) {
        this.i = i;
    }

    public int myFun(int i) {
        return i * 10;
    }

    //重写call方法
    @Override
    public Integer call() throws Exception {
        // TODO Auto-generated method stub
        System.out.println(Thread.currentThread().getName() + "-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
        return myFun(i);
    }
}

public class ThreadDemo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // TODO Auto-generated method stub

        Callable<Integer> oneCallable = new Tickets<Object>(2);
        FutureTask<Integer> oneTask = new FutureTask<Integer>(oneCallable);
        Thread t = new Thread(oneTask);
        t.start();
        System.out.println(oneTask.get());

        Callable<Integer> callable = new Tickets<Object>(3);
        FutureTask<Integer> task = new FutureTask<Integer>(callable);
        Thread t1 = new Thread(task);
        t1.start();
        System.out.println(task.get());

        //main线程
        System.out.println(Thread.currentThread().toString());
    }
}


Thread-0-->我是通过实现Callable接口通过FutureTask包装器来实现的线程
20
Thread-1-->我是通过实现Callable接口通过FutureTask包装器来实现的线程
30
Thread[main,5,main]

}

ExecutorService、Callable都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,还有Future接口也是属于这个框架,有了这种特征得到返回值就很方便了。 通过分析可以知道,他同样也是实现了Callable接口,实现了Call方法,所以有返回值。这也就是正好符合了前面所说的两种分类

Callable<Integer> oneCallable = new Tickets<Object>(2);
    FutureTask<Integer> oneTask = new FutureTask<Integer>(oneCallable);
    Thread t = new Thread(oneTask);
    t.start();
    System.out.println(oneTask.get());

    Callable<Integer> callable = new Tickets<Object>(3);
    FutureTask<Integer> task = new FutureTask<Integer>(callable);
    Thread t1 = new Thread(task);
    t1.start();
    System.out.println(task.get());

    //main线程
    System.out.println(Thread.currentThread().toString());
}

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。get方法是阻塞的,即:线程无返回结果,get方法会一直等待。 

线程实现方式4:通过线程池创建线程

创建线程池底层均会调用ThreadPoolExecutor构造函数


public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue) {

    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
        Executors.defaultThreadFactory(), defaultHandler);

}


corePoolSize:核心线程最大数量,通俗点来讲就是,线程池中常驻线程的最大数量。线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程;如果超过corePoolSize,则新建的是非核心线程。

maximumPoolSize:线程池中运行最大线程数(包括核心线程和非核心线程)

keepAliveTime:线程池中空闲线程(仅适用于非核心线程)所能存活的最长时间。当需要执行的任务很多,线程池的线程数大于核心池的大小时,keepAliveTime才起作用。

unit:存活时间单位,与keepAliveTime搭配使用(TimeUnit.DAYS,TimeUnit.HOURS,TimeUnit.MINUTES,TimeUnit.MILLISECONDS,TimeUnit.MICRODECONDS)

workQueue:存放任务的阻塞队列(维护着等待执行的 Runnable对象。当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务)。

handler:线程池饱和策略

java面试系列--java中线程的实现方式_线程池

线程池处理新提交任务流程

1.判断线程池中核心线程数是否已达阀值corePoolSize,若否,则创建一个新核心线程执行任务

2.若核心线程数已达阀值corePoolSize,判断阻塞队列workQueue是否已满,若未满,则将新任务添加进阻塞队列

3.若已满,判断线程池中线程数是否达到阀值maximumPoolSize,若否,则新建一个非核心线程执行任务。若达到阀值,则执行线程池饱和策略

java创建线程池的四种方式:

newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行

newSingleThreadExecutor 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行


package org.example.myThread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadDemo05 {
    private static int POOL_NUM = 5;     //线程池数量

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < POOL_NUM; i++) {
            RunnableThread thread = new RunnableThread();
            Thread.sleep(100);
            executorService.execute(thread);
        }
        //关闭线程池
        executorService.shutdown();
    }

}

class RunnableThread implements Runnable {
    @Override
    public void run() {
        System.out.println("通过线程池方式创建的线程:" + Thread.currentThread().getName() + " ");
    }
}

通过线程池方式创建的线程:pool-1-thread-1 
通过线程池方式创建的线程:pool-1-thread-2 
通过线程池方式创建的线程:pool-1-thread-3 
通过线程池方式创建的线程:pool-1-thread-1 
通过线程池方式创建的线程:pool-1-thread-2

}

举报

相关推荐

0 条评论