课程学习--JavaSE--后端(第6期):线程

线程

线程指的是进程中一个单一顺序的控制流。

程序、进程及线程

程序(program)

用某种编程语言(java、python等)编写,能够完成一定任务或者功能的代码集合,是指令和数据的有序集合,是一段静态代码

进程(progress)

正在运行的程序,CPU为其开辟一定的内存空间。

线程(Thread)

线程指的是进程中一个单一顺序的控制流,是利用CPU调度资源和执行计算的最小单元。

线程的种类

用户线程

针对用户服务的线程,由用户程序决定何时结束线程。

守护线程

守护用户服务的线程,随着用户线程的结束而结束。

// 将用户线程变成守护线程。
thread.setDaemon(true)

操作系统中线程的状态

新建(New):声明线程后。

就绪(Ready):线程正在等待使用CPU,经调度程序调用之后可进入running状态。

运行(Running):线程正在使用CPU。

阻塞(Waiting):线程经过等待事件的调用或者正在等待其他资源(如I/O)。

死亡(Terminated):线程完成全部工作或强制终止或出现异常。

Java线程的状态

public enum State {
    /**
     * 当线程类被实例化后的状态。
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * 当调用start方法后的状态。
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE,

    /**
   	 * 当线程等待同步锁释放以抢占该锁进入同步方法块的一种状态。
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * 当调用wait方法后的状态。
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>A thread in the waiting state is waiting for another thread to
     * perform a particular action.
     *
     * For example, a thread that has called <tt>Object.wait()</tt>
     * on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
     * that object. A thread that has called <tt>Thread.join()</tt>
     * is waiting for a specified thread to terminate.
     */
    WAITING,

    /**
     * 当调用sleep方法后的状态。
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of
     * the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     * 当线程完成所有所需执行的程序后自我终结的状态。
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
    TERMINATED;
}

创建线程的基本方式

方式一:从Thread派生一个自定义类,然后覆写run()方法。

public class Main {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start(); // 启动新线程
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("start new thread!");
    }
}

方式二:实现Runnable接口,创建Thread实例时,传入一个Runnable实例。

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start(); // 启动新线程
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("start new thread!");
    }
}

线程的常用方法

/**
 * Thread Method
 * 1.start--启动
 * 2.run--运行线程所要执行的的任务,由Java底层调用,而不是程序本身调用。
 * 3.currentThread--获取当前线程
 * 4.getName--获取线程名
 * 5.setName--设置线程名
 * 6.yield--让步
 * 7.join--抢占
 * 8.stop--结束
 * 9.sleep--阻塞(定时)
 * 10.isAlive--查看激活状态
 * 11.getPriority--高优先级会抢占低优先级,但是并发数要足够多。因为只是一个概率,并不是绝对抢占。
 * 12.setPriority--设置优先级
 *
 * 以下是Object类的方法
 * 13.wait--阻塞(一种等待锁,进入同步方法之前的状态)
 * 14.notify--随机唤醒
 * 15.notifyAll--唤醒所有处于
 */

线程的优先级

Java默认的线程优先级为5,范围是1~10。线程的调度策略采用抢占式。优先级高的线程比优先级低的线程会有更大的几率优先执行,但只是几率大,并不是一定大。

线程的同步

同步与异步的概念

同步:如队列一般顺序前进。

异步:如赛跑一般并发前进。

实现同步的方法

线程的同步可以利用对象枷锁来实现。

  1. 同步代码块

    synchronized(ObjLock) {
        // 获取锁
    }
    // 释放锁
    
  2. 同步方法

    synchronized void method() {
    }
    

死锁

可重入锁:能被已经get到其他锁的线程获取的锁。

死锁:多线程各自持有不同的锁,并互相试图获取对方已持有的锁,导致无限等待。

Callable接口

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

优点:有返回值、支持泛型、可以抛出异常

Future接口

Future可以对具体的Runnable、Callable任务的执行结果进行取消、查询及获取。

FutureTask实现类

FutureTask是Future唯一的实现类,其同时实现了Runnable,Future接口。既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。下图为FutureTask的UML图:

Future

package cn.charliejohn.thread;

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

// 1.实现Callable接口
class NewThread implements Callable {
    // 2.重写call方法
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

public class CallableDemo {
    public static void main(String[] args) {
        // 3.实例化Callable对象
        NewThread newThread = new NewThread();
        // 4.利用futureTask管理线程
        FutureTask<Integer> futureTask = new FutureTask<Integer>(newThread);
        // 5.启动线程
        new Thread(futureTask).start();
        try {
            // get<--call
            // get的目的是获取返回值
            int sum = futureTask.get();
            System.out.println("sum:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

线程池

使用线程池主要有以下三个原因:

  1. 创建/销毁线程需要消耗系统资源,线程池可以复用已创建的线程
  2. 控制并发的数量。并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃。(主要原因)
  3. 可以对线程做统一管理
ThreadPoolExecutor

下图为线程池常见子类ThreadPoolExecutor的UML图。

ThreadPoolExecutor

构造方法
// 五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

// 六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

// 六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

// 七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

涉及到5~7个参数,参数的具体意义如下。

  • int corePoolSize:该线程池中核心线程数最大值

    核心线程:线程池中有两类线程,核心线程和非核心线程。核心线程默认情况下会一直存在于线程池中,即使这个核心线程什么都不干(铁饭碗),而非核心线程如果长时间的闲置,就会被销毁(临时工)。

  • int maximumPoolSize:该线程池中线程总数最大值

    该值等于核心线程数量 + 非核心线程数量。

  • long keepAliveTime非核心线程闲置超时时长

    非核心线程如果处于闲置状态超过该值,就会被销毁。如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。

  • TimeUnit unit:keepAliveTime的单位。

TimeUnit是一个枚举类型 ,包括以下属性:

NANOSECONDS : 1微毫秒 = 1微秒 / 1000 MICROSECONDS : 1微秒 = 1毫秒 / 1000 MILLISECONDS : 1毫秒 = 1秒 /1000 SECONDS : 秒 MINUTES : 分 HOURS : 小时 DAYS : 天

  • BlockingQueue workQueue:阻塞队列,维护着等待执行的Runnable任务对象

    常用的几个阻塞队列:

    1. LinkedBlockingQueue

      链式阻塞队列,底层数据结构是链表,默认大小是Integer.MAX_VALUE,也可以指定大小。

    2. ArrayBlockingQueue

      数组阻塞队列,底层数据结构是数组,需要指定队列的大小。

    3. SynchronousQueue

      同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。

    4. DelayQueue

      延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。

ExecutorService接口

线程池的接口,常用子类ThreadPoolExecutor

void execute(Runnable command);
<T> Future<T> submit(Callable<T> task);
Executors工具类

用于创建并返回不同类型的线程池。

/**
 * 缓存
 * 因为corePoolSize为0的关系,不创建核心线程,线程池最大为Integer.MAX_VALUE。
 * 当需要执行很多短时间的任务时,CacheThreadPool的线程复用率比较高,会显著的提高性能。
 * 而且线程60s后会回收,意味着即使没有任务进来,CacheThreadPool并不会占用很多资源。
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
/**
 * 定长
 * 核心线程数量和总线程数量相等,都是传入的参数nThreads,所以只能创建核心线程,不能创建非核心线程。
 * 因为LinkedBlockingQueue的默认大小是Integer.MAX_VALUE,故如果核心线程空闲,则交给核心线程处理;
 * 如果核心线程不空闲,则入列等待,直到核心线程空闲。
 */
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

/**
 * 单例
 * 有且仅有一个核心线程( corePoolSize == maximumPoolSize=1),使用了LinkedBlockingQueue(容量很大),
 * 所以,不会创建非核心线程。所有任务按照先来先执行的顺序执行。
 * 如果这个唯一的线程不空闲,那么新来的任务会存储在任务队列里等待执行。
 */
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

/**
 * 定时
 * 创建一个定长线程池,支持定时及周期性任务执行。
 */
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}
实例
package cn.charliejohn.thread;

import java.util.concurrent.*;

class RunnableThread implements Runnable{
	int sum = 0;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName()+":i="+i);
            sum+=i;
        }
        System.out.println("sum:" + sum);
    }
}

class CallableThread implements Callable {
	int sum = 0;
    @Override
    public Object call() throws Exception {

        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName()+":i="+i);
            sum+=i;
        }
        return sum;
    }
}

public class ThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(10);
        System.out.println(service.getClass());
        // ThreadPoolExecutor
        // 子类
        ThreadPoolExecutor executor = (ThreadPoolExecutor) service;
        // 类属性为变量
        executor.setCorePoolSize(15);
        // executor.setKeepAliveTime();
        // 适合runnable
        service.execute(new RunnableThread());
        // 适合callable
        Future future = service.submit(new CallableThread());
        System.out.println("FutureSum:" + future.get());
        service.shutdown();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

charliejohn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值