线程基本知识

Posted myarticles

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程基本知识相关的知识,希望对你有一定的参考价值。

线程的状态以及状态之间的切换

线程从创建到死亡有七个状态,分别是初始状态,准备运行,运行状态,阻塞状态,睡眠状态(超时等待状态),等待状态,死亡状态,关系如图所示:
技术图片

线程的创建

实现线程的几种方式:

  • 继承Thread类
  • 实现Runnable接口
  • 匿名内部类的方式
  • 带返回值的线程
  • 定时器
  • 线程池实现
  • Lambda表达式实现

继承Thread

// 无名
public class Demo01 extends Thread {
    @Override
    public void run() {
        System.out.println(getName() + "执行了。。。");
    }

    public static void main(String[] args) {
        Demo01 d1 = new Demo01();
        Demo01 d2 = new Demo01();
        d1.start();
        d2.start();
    }
}

// 有名
public class Demo01 extends Thread {
    @Override
    public void run() {
        System.out.println(getName() + "执行了。。。");
    }

    public Demo01(String name) {
        super(name);
    }

    public static void main(String[] args) {
        Demo01 d1 = new Demo01("first thread");
        Demo01 d2 = new Demo01("second thread");
        d1.start();
        d2.start();
    }
}

实现Runnable接口

实现了runnable接口的类是作为一个线程任务存在的,需要将实例化后的对象传入Thread中

public class Demo02 implements Runnable {
    @Override
    public void run() {
        System.out.println("线程启动");
    }

    public static void main(String[] args) {
        Thread t = new Thread(new Demo02());
        t.start();
    }
}

匿名内部类实现

// 继承Thread类的方式
public class Demo03 {
    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                System.out.println("启动线程。。。");
            }
        }.start();
    }
}

// 实现Runnable接口的方式
public class Demo03 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程启动。。。");
            }
        }).start();
    }
}

带返回值的线程

上面创建的线程都是不带返回值以及不能抛异常的线程,接下来实现一种带返回值的线程

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

public class Demo04 implements Callable<Integer> {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Demo04 d4 = new Demo04();

        FutureTask<Integer> task = new FutureTask<>(d4);
        Thread t = new Thread(task);
        t.start();
        System.out.println("计算结果是:" + task.get());
    }


    @Override
    public Integer call() throws Exception {
        System.out.println("正在进行紧张的计算。。。");
        Thread.sleep(3000);
        return 1;
    }
}

定时器

定时器Timer通过schedule方法主席那个定时任务,可以指定延时多久执行,每隔多久执行,甚至是指定一个日期执行;或者是指定第一次执行的日期,然后每隔多久执行等等。

import java.util.Timer;
import java.util.TimerTask;

public class Demo05 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 定时器执行的任务
                System.out.println("定时器正在执行");
            }
        }, 0, 1000);   // 延时0秒执行,每隔一秒执行一次
    }
}

基于线程池实现

线程池用于降低线程创建和销毁的开销,需要线程时直接从池子里面拿,不需要时也不会销毁而是放回池子里面等待下一次使用,典型的拿空间换时间。

Executors创建

线程池最顶级的接口是Executor,Executor里面只有一个execute方法,这个方法用于执行线程任务,但是线程池在执行完所有的任务后不会停止,需要执行shutdown()方法,但是这个方法没有在Executor中声明,而是在ExecutorService类中实现的,所以使用ExecutorService方法接收线程池对象从而执行shutdown()方法关闭线程池

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

public class Demo06 {
    public static void main(String[] args) {
        ExecutorService e = Executors.newFixedThreadPool(10);

        for (int  i = 0; i < 100; i ++) {
            e.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
        e.shutdown();
    }
}

上面是通过Executors静态工厂创建的线程池,Executors一共提供了五个核心方法,分别是:

  • Executors.newWorkStealingPool():这个方法在JDK1.8中引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争
  • Executors.newCachedThreadPool():maximumPoolSize最大可以到Integer.MAX_VLUE,线程数不够会自动增加,比较弹性。存在OOM异常,能够回收不工作的线程,keepAliveTime默认60,也就是60秒回收一个空闲线程
  • Executors.newScheduledThreadPool():线程数最大至Integer.MAX_VLUE,同样存在OOM风险,支持定时及周期性执行任务,优于Timer,不会回收不工作的线程
  • Executors.newSingleThreadExecutor():创建一个单线程的线程池,相当于单线程串行执行所有任务,保证按任务的提交顺序依次执行
  • Executors.newFixedThreadPool():输入的参数就是固定的线程数,既是核心线程数也是最大线程数,不存在空闲线程,所以keepAliveTime为0

使用ThreadPoolExecutor创建

一般不推荐使用Executors创建二十建议使用ThreadPollExecutor,因为Executors中的方法允许的请求队列长度是Integer.MAX_VALUE,可能会导致堆积大量的请求,从而导致OOM

ThreadPoolExecutor的构造方法由7个参数:

  • corePoolSize:表示核心线程数,核心线程在任务执行完毕后不会销毁
  • maximumPoolSize:线程池能够容纳的最大线程数,必须大于1,如果执行的任务数超过了该值,多出的任务数会被放入队列中等待执行
  • keepAliveTime:表示线程池中允许线程空闲的时间,当空闲时间达到keepAliveTime时该线程就会被销毁,直到池中只有corePoolSize数量的线程为止。如果想让核心线程超时后也被回收,只需要将ThreadPoolExecutor的allowCoreThreadTimeOut变量设置为true
  • TimeUnit:表示keepAliveTime的时间单位,通常时TimeUnit.SECONDS
  • workQueue:缓存队列,当请求的任务数大于maxiumPoolSize后,超过的任务会进入BlockingQueue阻塞队列中
  • threadFactory:用于生产一组相同任务的线程,线程的命名就是通过给这个factory增加组名前缀来实现的
  • handler:执行拒绝策略的对象,当执行的任务数超过第五个参数workQueue的缓存限制时,及就可以使用该策略处理请求,比方说将请求转向某个提示页面表示当前执行任务数过多

上面的队列、线程工厂、拒绝处理服务都必须有实例对象,但是再实际编程中都是通过Executors这个线程池静态工厂提供默认的实现,Executors用法上面已经说明,那么现在使用ThreadPoolExecutor来创建一个线程池

第一步:实现队列,线程工厂,拒绝服务
// 线程工厂
class UserThreadFactory implements ThreadFactory {
    private final String namePrefix;
    private final AtomicInteger nextId = new AtomicInteger(1);

    UserThreadFactory(String name) {
        namePrefix = "该线程来自:" + name + "----这是该线程池中的第";
    }
    /**
     * 该方法用于创建线程池中的线程
     * @param r
     * @return
     */
    @Override
    public Thread newThread(Runnable r) {
        String name = namePrefix + nextId.getAndIncrement()  + "个线程";
        Thread thread = new Thread(null, r, name, 0);
        System.out.println(thread.getName());   // 打印线程名称
        return thread;
    }
}

// 拒绝服务实现
class UserRejectHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("任务太多啦,被拒绝了" + executor.toString());
    }
}
第二步:实现线程任务
class Task implements Runnable {

    @Override
    public void run() {
        System.out.println("执行中。。。");
    }
}
创建线程池
public class Demo07 {
    public static void main(String[] args) {
        // 设置了缓存队列的大小为2
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(2), new UserThreadFactory("线程池工厂一"), new UserRejectHandler());
        for (int i = 0; i < 200; i++) {
            tpe.execute(new Task());
        }
    }
}

Lambda表达式实现

使用parallelStream()实现

以上是关于线程基本知识的主要内容,如果未能解决你的问题,请参考以下文章

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

为啥基于锁的程序不能组成正确的线程安全片段?

Java——线程池

多线程 Thread 线程同步 synchronized

活动到片段方法调用带有进度条的线程

Java线程池详解