谈起java多线程,我们首先想到的肯定是如何实现java多线程。下面我们就来聊一聊java多线程的实现方式。

一、java多线程的实现方式

1、继承Thread类,重写run方法;
2、实现Runnable接口,实现run方法;
3、实现Callable接口,实现call()方法;
具体用法如下:
第一步:创建Callable接口的实例化子对象;
第二步:将Callable对象传入FutureTask构造函数中,创建FutureTask对象;
第三步:将FutureTask对象传入Thread的构造函数中,创建Thread对象;
第四步:调用start()方法,启动线程;

代码实例:

package com.javabase.multithread;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * @author
 * @date 2020/12/19
 * java中实现多线程的三种方法;
 * 四中线程池的比较;
 * 如何自定义并使用线程池
 */
public class MultiThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //(1)继承Thread类型,重写run方法
        //new Thread1().start();

        //(2)实现了Runnable接口,实现run方法
        //new Thread(new Thread2()).start();

        //(3)实现Callable接口,实现call方法;
        // ExecutorService来使用——推荐用法
        ExecutorService executorService =  Executors.newCachedThreadPool();
        Thread3 thread3 = new Thread3();
        Future<Integer> future = executorService.submit(thread3);
        //停止接收新任务,原来的任务继续执行
        executorService.shutdown();
        //get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
        //get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null
        Integer result = future.get();
        System.out.println(result);

        //使用Thread
        FutureTask<Integer> futureTask = new FutureTask<>(thread3);
        Thread thread4 = new Thread(futureTask);
        thread4.start();
        System.out.println("使用Thread的方式获取结果===="+futureTask.get());
        
    }
}
package com.javabase.multithread;

/**
 * @author
 * @date 2020/12/19
 * 继承Thread类型,重写run方法
 */
public class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println("线程1启动");
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1继承了Thread类,一直在工作。。。。。。。。");
        }
    }
}
package com.javabase.multithread;

/**
 * @author 
 * @date 2020/12/21
 * 实现Runnable接口,实现run方法
 */
public class Thread2 implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("线程2启动");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2实现了Runnable接口,重写了run方法,一直在工作");
        }
    }
}
package com.javabase.multithread;

import java.util.concurrent.Callable;

/**
 * @author
 * @date 2020/12/21
 * 实现Callable接口,实现call方法;
 * 接口中的泛型就是返回的结果类型
 */
public class Thread3 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("线程3开始启动。。。。。。");
        Thread.sleep(10000);
        int sum =0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}
二、通过线程池的方式实现多线程,这也是在java开发项目中最常用的方法。

按道理说,我们前面已经学习了三种创建多线程的方法,为什么在项目中还要使用线程池的方法来创建线程呢?主要是因为这种方式有如下优点:
线程池:顾名思义就是存放线程的池子,系统已经把线程创建好了,使用的时候直接拿就好了。其实多线程的创建与销毁是比较消耗资源的,使用线程池减小了线程创建与销毁的开销。
线程池既是存放线程的地方,也对线程的生命周期进行管理。

既然谈到了线程池,我们不得不说常用的四种线程池类型了。
(1)缓存类型的线程池(newCachedThredPool):线程池可以根据任务的多少,自动控制线程的数量,还是挺爽的用法。

//创建缓存类型的线程池:
        //线程池会根据任务的多少自动控制线程数量
        ExecutorService executorService = Executors.newCachedThreadPool();

缺点:线程池中最大线程数量为Integer.MAX_VALUE,会造成OOM。

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

(2)固定数量的线程池(newFixedThreadPool):创建固定数量的线程,放在池子里面,当任务数量多余线程数量时,需要排队;少于的时候就会造成资源浪费。

//创建100个线程
ExecutorService executorService1 = Executors.newFixedThreadPool(100);

缺点:使用LinkedBlockingQueue,这是无界队里,可能会造成OOM。

 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

(3)创建可以定时或者延时执行的线程池(newScheduledThreadPool):

 //long initialDelay:表示延迟时间                                                                
 //long period:表示运行次数                                                                      
 //TimeUnit unit:表示时间单位   
 //创建的时候需要传入创建线程的数量                                                                               
 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(100);
 //表示延迟11s,每11s执行一次,输出"实现了多线程"                                                             
 scheduledExecutorService.scheduleAtFixedRate(new Runnable() {                             
     @Override public void run() {                                                         
         System.out.println("实现了多线程");                                                     
                                                                                           
     }                                                                                     
 }, 1, 11, TimeUnit.SECONDS);     

缺点:线程池中最大线程数量为Integer.MAX_VALUE,会造成OOM。

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

(4)创建单例线程池(newSingleThreadExecutor):它只会创建一个线程池,用这个线程池来完成任务。

ExecutorService executorService2 = Executors.newSingleThreadExecutor(); 

缺点:使用LinkedBlockingQueue,这是无界队里,可能会造成OOM。

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
三、推荐创建线程的方法
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

各个字段的含义:
(1)corePoolSize:核心线程数
(2)maximumPoolSize:最大线程数
(3)keepAliveTime:线程空闲存活时间
(4)unit:线程空闲存活时间单位
(5)workQueue:任务排队队列
(6)threadFactory:线程生产工厂
(7)handler:线程拒绝策略

线程的四中拒绝策略

(1)rejected = new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务抛出异常
(2) rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务不异常
(3)rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之后再尝试加入队列
(4) rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务

四、多线程的状态转换过程

Java线程的6种状态及切换

五、补充

线程的优先级—priority

优先级不严格代表执行顺序,只是代表执行线程的概率大小。

死锁形成的原因:

1、资源的互斥:甲拥有了A资源后,乙无法在拥有A资源;
2、不可剥夺性:甲拥有了A资源后,乙无法从甲那里争夺到A资源;
3、请求保持:甲拥有了A资源之后,又去请求B资源;在获取不到B资源的情况下,依然保持对A资源的拥有;
4、循环等待:甲获得了A资源,还缺B资源;乙获得了B资源,还缺A资源;