文章目录
线程
线程指的是进程中一个单一顺序的控制流。
程序、进程及线程
程序(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。线程的调度策略采用抢占式。优先级高的线程比优先级低的线程会有更大的几率优先执行,但只是几率大,并不是一定大。
线程的同步
同步与异步的概念
同步:如队列一般顺序前进。
异步:如赛跑一般并发前进。
实现同步的方法
线程的同步可以利用对象枷锁来实现。
-
同步代码块
synchronized(ObjLock) { // 获取锁 } // 释放锁
-
同步方法
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图:
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();
}
}
}
线程池
使用线程池主要有以下三个原因:
- 创建/销毁线程需要消耗系统资源,线程池可以复用已创建的线程。
- 控制并发的数量。并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃。(主要原因)
- 可以对线程做统一管理。
ThreadPoolExecutor
下图为线程池常见子类ThreadPoolExecutor的UML图。
构造方法
// 五个参数的构造函数
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任务对象。
常用的几个阻塞队列:
-
LinkedBlockingQueue
链式阻塞队列,底层数据结构是链表,默认大小是
Integer.MAX_VALUE
,也可以指定大小。 -
ArrayBlockingQueue
数组阻塞队列,底层数据结构是数组,需要指定队列的大小。
-
SynchronousQueue
同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。
-
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();
}
}