线程与进程
- 线程
- 一个进程可以产生多个线程
- 多个线程共享一块内存空间与系统资源
- 进程
- 程序的一次执行过程
- 系统运行程序基本单位
多线程
- 概念
- 多个线程同时/交替运行
- 如果是单核CPU则顺序执行,即交替执行
- 如果是多核CPU则同时运行
- 优点
- 可以将占据长时间的程序放到后台处理
- 加快程序运行速度
怎么使用多线程
- 继承Thread类,重写run()方法
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
public class Test{
public static void main(String args[]) {
Thread thread = new MyThread();
thread.start();
}
}
- 实现Runnable接口,实现run()方法 推荐使用
public class MyRunnable extends Runnable {
@Override
public void run() {
super.run();
System.out.println("MyRunnable");
}
}
public class Test{
public static void main(String args[]) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
- 实现Callable<返回对象>接口,实现call()方法,可以使用FutureTask<>接受返回值
public class MyCallable extends Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("MyCallable");
return 1;
}
}
public class Test{
public static void main(String args[]) {
Callable callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new MyThread(futureTask);
thread.start();
System.out.println(futureTask.get());
}
}
简单的共享属性
public class MyThread extends Thread {
int count = 0;
@Override
public synchronized void run() {
super.run();
count++;
System.out.println("MyThread");
}
}
public class Test{
public static void main(String args[]) {
Thread thread = new MyThread();
Thread a = new Thread(thread, "A");
Thread b = new Thread(thread, "B");
a.start();
b.start();
}
}
终止线程的方法
- interrupt() -> interrupted()
- return;
线程状态
常用方法
方法名 | 描述 |
---|---|
currentThread() | 返回当前执行线程的引用 |
getId() | 返回此线程的标识符 |
setName(String name) | 更改线程名称 |
getName() | 返回此线程的名称 |
setPriority(int newPriority) | 更改线程优先级 |
getPriority() | 返回此线程的优先级 |
siAlive() | 当前线程是否处于活动状态 |
sleep(long millis) | 是当前线程限期休眠 |
interrupt() | 为这个线程打上中断标识 |
interrupted() | 测试当前线程是否为中断状态,若为中断状态则中断该线程并清除状态标识 |
isInterrupted() | 测试当前线程是否有中断标识 |
isDaemon() | 测试当前线程是否为守护线程 |
setDaemon(bolean on) | 设置当前线程为守护线程/用户线程 |
join() | 等待该线程终止,再返回调用这个方法的线程 |
yield() | 放弃当前CPU资源,让给其他任务.放弃时间不确定即有可能放弃后马上获取到CPU时间片 |
优先级
- 优先级具有随机性,并不一定优先级高的先执行
- 分级
- Thread.MIN_PRIORITY(1)
- Thread.ORM_PRIORITY(5) 默认
- Thread.MAX_PRIORITY(10)
守护线程
- 运行在后台为用户线程服务
- 当所有用户线程都结束,守护线程会随着JVM结束
- 例如: 垃圾回收线程
- 注意:
- setDeamon(true)需要在start()之前执行
- 守护线程中产生的线程也是守护线程
- 读写/逻辑运算不能交给守护线程
synchronized
- 对象锁:锁定的是对象
- synchronized public void method() {}
- synchronized(this){…}: 代码块,synchronized锁定方法(尤其是比较大的方法)会导致另一个线程长时间的等待,最好使用synchronized锁定代码块
public class Task {
private String data1;
private String data2;
public void do() {
system.out.println("start");
synchronized(this) {
data1 = "data1" + Thread.currentThread().getName();
data2 = "data2" + Thread.cyrrentThread().getName();
}
system.out.println(data1);
system.out.println(data2);
}
}
public MyThread extends Thread {
private Task task;
public MyThread(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
task.do();
system.out.println("end");
}
}
public Run {
public static void main(String args[]) {
Task task = new Task;
MyThread thread1 = new MyThread(task);
Mythread thread2 = new Mythread(task);
thread1.start();
thread2.start();
}
}
- synchronized(object){…}: 使用相同的对象监视器可以使得运行同步,若使用不同的对象监视器则会与没有同步块一样
public class MyObject {
}
public class Task {
private String data1;
private String data2;
public void do(MyObject object) {
sychronized(object) {
try{
system.out.println("start");
data1 = "data1" + Thread.currentThread().getName();
data2 = "data2" + Thread.cyrrentThread().getName();
system.out.println(data1);
system.out.println(data2);
} finaly { }
}
}
}
public MyThread extends Thread {
private Task task;
private MyObject object;
public MyThread(Task task, MyObject object) {
super();
this.task = task;
this.object = object;
}
@Override
public void run() {
super.run();
task.do(object);
system.out.println("end");
}
}
public Run {
public static void main(String args[]) {
Task task = new Task;
MyObject object = new MyObject;
MyThread thread1 = new MyThread(task, object);
Mythread thread2 = new Mythread(task, object);
thread1.start();
thread2.start();
}
}
- 类锁:锁定的是类(静态方法)
- public static void method() { synchronized(className.class){…}}
- synchronized public static void method(){}
- 同步不具备继承性:父类的同步方法,子类继承重写不具备同步需重写添加
- 一个对象只有一个锁:当A线程有Object对象的锁,当B线程调用Object对象中synchronized方法时需要等待,若只是调用非synchronized方法则可以异步调用.
- 锁重入机制:只要线程拥有某个对象锁,则可以在拥有这个锁的期间不断重新获得这个锁
- 异常自动释放锁:当线程执行出现异常,此线程持有的锁会自动释放
volatile
- 被volatile修饰的成员变量,每次被线程访问时都将从主内存中重读值,发生变化时都将写到主内存中.保证数据的可见性
- 在while语句中加了输出/sleep()语句,JVM会尽力的保证内存的可见性即使没有volatile修饰关键字,也可以退出while循环
- 不能保证原子性
wait/notify机制
- 线程通过调用对象Object.wait()使线程进入等待状态,而其他线程通过Object.notify()唤醒线程(这个Object类似于一个lock)
- 方法
方法名 | 描述 |
---|---|
wait() | 调用该方法的线程释放资源,进入等待队列 |
wait(long timeout) | 超时等待, 超出时间就返回不需要notify |
wait(long timeout, int nanos | 更好的操作粒度 |
notify() | 随机唤醒一个等待这个资源的线程 |
notifyAll() | 唤醒所有等待这个资源的线程,是按优先级/随机,看JVM |
- 与synchronized相配合,将Object作为关键字.wait()方法会释放同步的锁.notify()不会释放同步的锁.
- 如果wait()状态遇到interrupt标识会报InterruptException
ThreadLocal
- 线程本地变量
- 在不同线程中调用同一个ThreadLocal类的值(方法)具有隔离性
- ThreadLocal其实是操作每个线程的ThreadLocalMap
- InheritableThreadLocal类: 是ThreadLcoal的升级,如果父线程使用过该对象,则其子线程将延续使用父线程的记录.
- 方法
方法名 | 描述 |
---|---|
get() | 返回当前线程中此变量的值 |
set(T value) | 设置当前线程中此变量的值 |
remove() | 删除当前线程中此变量的值 |
initialValue() | 返回当前线程此变量的初始值 |
ThreadGroup
- 线程组: 批量管理线程/线程组
- 初始化
- ThreadGroup(String name)
- ThreadGroup(ThreadGroup parent, String name)
- 线程启动后才能回归到制定线程组
组件
- CountDownLatch:机制是多个线程达到预期状态通过CountDownLatch方法将通知时间合一,使等待线程开始任务(多个线程)
- Semaphore:并发管理器,控制部分代码,通过acquire()获取信号,release()归还信号
- Exchanger:两个线程间的数据交换,需要两个线程都运行到交换处再交换(先到的阻塞)
- CyclicBarrier:像一堵墙让多个线程在这等待,直到所有线程到达再一起继续
Lock
- Lock接口的实现类
- ReentrantLock(排他锁)
- ReenteantReadWriteLock(读写锁)
具有两个锁,一个共享锁(读操作),一个排它锁(写操作)
- Lock接口的基本方法
方法名 | 描述 |
---|---|
void lock() | 获得锁,若锁不可用则休眠知道获得锁. 最好在try方法块外调用 |
void unlock() | 释放锁. 在finally中调用 |
boolean tryLock() | 尝试获取锁,若可用则获取锁并返回true,若不可用则返回false |
boolean tryLock(long time, TimeUnit unit) | 超时获取锁. 1.在time时间内获取锁 2.time时间内被中断 3.超出time返回false |
void lockInterruptibly() | 获取锁,如果可用就立即返回.若不可用则休眠(可以中断当前线程),直至获得锁 |
Condition newCondition() | 获得等待通知组件. 组件与当前锁绑定,只有拥有锁才能调用改组件的wait()方法并将释放锁 |
- ReentrantLock类方法
方法名 | 描述 |
---|---|
ReentrantLock() | |
ReentrantLock(boolean fair) | 公平锁(FIFO)/非公平锁(随机,Lock默认) |
int getHoldCount() | 查询当前线程保持这个锁的个数 |
protected Thread getOwner() | 返回当前拥有此锁的线程 |
protected Collection getQueuedThreads() | 返回正在等待此锁的线程的集合 |
int getQueueLength() | 返回等待此锁线程数的估计 |
boolean hasWaiters(Condition condition) | 查询是否有在某种等待条件下等待此锁的线程 |
protected Collection getWaitingThreads(Condition condition) | 返回在某种等待条件下等待此锁的线程的集合 |
int getWaitQueueLength(Condition condition) | 返回在某种等待条件下等待此锁的线程的集合 |
boolean hasQueuedThread(Thread thread) | 查询特定线程是否在等待获取锁 |
boolean hasQueuedThreads | 查询是否有线程在等待获取锁 |
boolean isFair() | 查询此锁是否是公平的 |
boolean isHeldByCurrentThread() | 查询当前线程是否持有此锁 |
boolean isLocked() | 查询此锁是否有任何线程持有 |
- ReentrantReadWriteLock类方法
方法名 | 描述 |
---|---|
ReentrantReadWriteLock() | |
ReentrantReadWriteLock(boolean fair) | 公平锁(FIFO)/非公平锁(随机,Lock默认) |
Condition接口
- 类似于wait()/notify(), 可以实现多路通知(一个Lock可以创建多个Condition)
- 方法
方法名 | 描述 |
---|---|
void await() | 相当于wait() |
boolean await(long time, TimeUnit unit) | 相当于wait(long timeout) |
singnal() | 相当于notify() |
signalAll() | 相当于notifyAll() |
线程池
- 作用: 限制/管理资源,维护统计信息.
- 优点:
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
- Executor接口框架
- 结构
- 任务: 实现Runnable/Callable接口.被ThreadPoolExecutor/ScheduledThreadPoolExecutor接受
- 任务的执行: 继承Executor接口的ExecutorService接口.ScheduledThreadPoolExecutor/ThreadPoolExecutor实现了ExecutorService接口. ScheduledThreadPoolExecutor实际上继承了ThreadPoolExecutor.
- 异步计算结果: Future接口及FutureTask类.用Runnable/Callable接口调用submit方法给ThreadPoolExecutor/ScheduledThreadPoolExecutor时会返回FutureTask对象
- 使用
- 主线程创建Runnable/Callable接口的对象(Executors工具类可以将两者互相转换)
- 将Runnable/Callable对象交给ExecutorService执行(ExecutorService.execute(Runnable runnable)无返回值 / ExecutorService.submit(Runnable/Callable)有返回值)
- 用FutureTask接受ExecutorService.submit(Runnable/Callable)的执行结果
- 主线程执行FutureTask.get()来获得返回值 / 执行FutureTask.cancel()取消此次任务的执行
- 结构
ThreadPoolExecutor类
构造函数
/** * 用给定的初始参数创建一个新的ThreadPoolExecutor。 * @param corePoolSiza 核心线程池的大小 * @param maximumPoolSize 最大线程池的大小 * @param keepAliveTime 当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime; * @param unit keepAliveTime参数的时间单位 * @param workQueue 等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列; * @param threadFactory 执行者创建新线程时使用的工厂 * @param handler RejectedExecutionHandler类型的变量,表示线程池的饱和策略。 * 如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。 * 线程池提供了4种策略: 1.AbortPolicy:直接抛出异常,这是默认策略; 2.CallerRunsPolicy:用调用者所在的线程来执行任务; 3.DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务; 4.DiscardPolicy:直接丢弃任务; */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,//可忽略 RejectedExecutionHandler handler//可忽略) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
创建ThreadPoolExecutor
- ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, [threadFactory], [handler])共四种方法
- Executors工具类生成(实际上调用了ThreadPoolExecutor的构造方法)
- Executors.newFixedThreadPool(int, [threadFactory])
即ThreadPoolExecutor(int, int, 0L, TimeUnit.MILLSECONDS, new LinkedBlockigQueue(), threadFactory)
可重用固定线程数的线程池, LinkedBlockigQueue是无边界的(容量Integer.MAX_VALUE),新任务都将直接到队列中
- Executors.newSignleThreadExecutor([threadFactory])
即ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), threadFactory)
单线程的线程池, 同样而是无边界的任务队列
- Executors.newCachedThreadPool([threadFactory])
即ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, new SynchronousQueue(), threadFactory)
无边界线程数的线程池
初试线程数为0,而线程数上限为无边界.
- Executors.newFixedThreadPool(int, [threadFactory])
使用ThreadPoolExecutor
public cass MyThreadPoolExecutor { public static void main(String args[]) { ExecutorService executor = new Executors.newFixedThreadPool(5); Runnable runnable = new MyRunnable(); executor.execute(runnable); executor.shutdown(); while(!executor.isTerminated()){} } }
- shutdown()与shutdownNow()
shutdown()表明关闭已经在Executor上调用不会再添加任务,但还会执行已经添加的任务
shutdownNow()会尝试终止正在运行的任务并返回未处理的任务List - isTerminated()与isShutdown()
isShutdown()当调用shutdown()/shutdownNow()返回true
isTerminated()当调用shutdown()并所有任务完成后返回true/调用shutdownNow()成功停止后返回ture
如果线程池任务是正常完成的则都返回false
- shutdown()与shutdownNow()
ScheduledThreadPoolExecutor类
- 主要是延迟/定时执行任务
- 相对于Timer来说ScheduledThreadPoolExecutor是多个线程而Timer只是单线程
- 获得: ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(int)
- 方法:
- schedule(Runnable runnable, long initialDelay, TimeUnit unit)
- scheduleAtFixedRate(Runnable runnable,long initialDelay,long period,TimeUnit unit) 延迟是两个任务开始的时间差 即initialDelay+n*period
- scheduleWithFixedDelay(Runnable runnable,long initialDelay,long delay,TimeUnit unit) 延迟是上个任务结束到下个任务开始的时间差
- 机制
- 调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()/scheduleWithFixedDelay()会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFuture接口的ScheduledFutureTask
- 线程从DelayQueue中获取ScheduledFutureTask,再执行任务
- 执行步骤
- 从DelayQueue中获取到期的ScheduledFutureTask(DelayQueue.take()),
- 分配线程执行这个ScheduledFutureTask
- 将这个ScheduledFutureTask的time变量更改为下次执行的时间
- 再将这个ScheduledFutureTask放回DelayQueue中(DelayQueue.add())
- 与ThreadPoolExecutor的不同
- 使用DelayQueue作为任务队列,DelayQueue封装了一个PriorityQueue,它会对任务按 1执行所需时间/2先后 进行排序.
- 获取任务方法不同
- 添加了额外的处理
各种线程池使用场景
- FixedThreadPool: 负载较重的服务器
- SingleThreadExecutor: 保证顺序执行各个任务且不在意时间的
- CachedThreadPool: 执行很多短期异步小程序
- ScheduledThreadPool: 多个后台周期执行任务
扩展
- Unsafe类
- java无法直接访问底层操作系统,只能通过native方法来进行操作
- Unsafe类是JVM的后门,提供硬件级别的原子操作
- 尽管Unsafe类中都是public但未经授信的代码无法使用此类
- Unsafe是CAS的核心
- Unsafe是LockSupport的依赖
- CAS(Compare and swap)
- java.util.concurrent包建立在CAS基础上
- 应用
- 原子类
- 方法compareAndSet(int a, int update)原子化操作
- 方法其实使用了unsafe类
- public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
- 原子类无法保证线程安全: 只有在单线程中原子类是安全的,多线程无法保证中间状态zhuangtai
- 原子类
- 原理
- 得到内存值V, 预期值A, 需要修改的值B
- 当A = V时, 将V = B,返回true,若什么都没做则返回false
- 即原先的值为A, 当前的值为V, 若V == A则表示此时的状态与原先的状态相同(虽然可能会有中间状态A-B-A)
- 通过加锁使得CAS是原子操作
- 通过volatile通知V的改变
- 如何解决A-B-A中间状态的出现? 带标记的原子引用AtomicStampedReference
- LockSupport类
- 类似于Object.wait()/notify()
- LockSupport不一定要在同步代码块中使用
- 个人看法: 类似于标记,可以先标记一个unpark(),当之后标记park()时可以直接执行.
死锁
- 两个线程互相等待对方释放锁
- 避免
- 避免一个线程同时获得多个锁
- 避免一个锁占有多个资源
- 尝试定时锁,Lock.tryLock(timeout)代替内部锁机制
- 对于数据库锁,加锁和解锁必须在同一个链接中,否则会出现解锁失败的情况
推荐