思维导图-多线程
2020-07-05 10:31:48 35 举报
AI智能生成
关于Java多线程思维导图整理
作者其他创作
大纲/内容
1.多线程基础
线程与进程
三种实现多任务的方式
多进行VS多线程VS混合模式
java采用多线程模式,实现多任务
运行顺序:
java程序运行,启动一个JVM进程
JVM启动主线程执行main()方法
在main方法中启动其他线程
2.创建新线程
两种方法
从Thread派生自定义类,
覆写run方法
创建`Thread`实例时,传入一个`Runnable`实例
4.已有的单个线程 相关
线程本身的状态
总的来说:
一个线程被 new出来, 只能调用一次 start()方法启动,执行线程中的run()方法,线程结束
线程总共状态:6种
线程等待: t.join()
在线程中,等待某个线程执行完成后再继续执行本身
比如,mian线程种执行t.join()那么,主线程必须等t线程执行完
Thread.sleep(20)
模拟并发执行的效果
Thread.setPriority(int n) //1~10, 默认5
这种方式是不能够确保一定先被执行的
中断线程
只是提出中断请求
提出中断请求后的中断响应
1.在线程的run()代码种使用while循环检查
isInterrupted()
2.中断标志位
在从Thread派生出线程子类的时候
用关键字volatile
public volatile boolean running = true;
同样在run()种使用while()
volatile定义的变量是线程间共享的
对处于t.join()的t线程执行t.interrupt()
t线程会立刻抛出InterruptedException
守护线程
Thread t = new MyThread();
t.setDaemon(true);
t.start();
JVM退出时,不必关心守护线程是否已结束
5.多个线程之间的问题
基础
多线程同时读写共享变量,数据不一致
例子:对一个计数类,两个线程分别对类种的共享变量进行增加\减少 操作
需要保证一组指令以"原子"方式执行
给对象加锁,获取锁以后才能继续执行后续代码synchronized()
"临界区"
概念:线程安全VS非线程安全
概念:可重入锁
一个线程可以多次获取同一个锁
同步方法
多个线程,同时执行某个类的实例的某个方法
需要对类的方法进行同步
称为:同步方法
对this加锁
例子:
一个具有增加和减少方法的计数Counter类
两种类的方法
普通类方法
类的静态方法
死锁:两个线程之间
两个线程各自持有不同的锁,然后各自试图获取对方手里的锁,造成了双方无限等待
各种锁
synchronized
语言层面提供的锁,不用考虑异常的问题
通过加锁和解锁的方式,解决多线程竞争的问题.
多线程协调的问题没有解决
问题描述
一直在while中循环,不能释放锁
多线程协调运行的原则
当条件不满足时,线程进入等待状态;
当条件满足时,线程被唤醒,继续执行任务
解决办法
wait()和notify()方法
使用要点
在synchronized内部调用wait();notify()或notifyAll()
必须在已获得的锁对象上调用wait();notify()或notifyAll()方法;
ReentrantLock
java代码实现的锁
类的private字段中定义
private final Lock lock = new ReentrantLock();
基本使用
必须先获取锁,
再进入try {...}代码块,最后使用finally保证释放锁
可以尝试获取锁,设置等待时间
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
...
} finally {
lock.unlock();
}
}
实现多线程的协调
实现类似于synchronized锁的wait()和notify()功能
通过锁的 condition 对象
(要绑定Lock实例)
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
condition的方法
(与synchronized类似)
await()会释放当前锁,进入等待状态;
signal()会唤醒某个等待线程;
signalAll()会唤醒所有等待线程;
ReadWriteLock
读写锁(悲观读锁)
特点
只允许一个线程写入
允许多个线程在没有写入的时候同时读取
适合读多写少
适用条件
同一个数据,
有大量线程读取,但仅有少数线程修改。
例子:
一个论坛的帖子,回复可以看做写入操作,它是不频繁的,但是,浏览可以看做读取操作,是非常频繁的,这种情况就可以使用ReadWriteLock
基本使用
创建一个ReadWriteLock实例,
分别获取读锁和写锁
private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
private final Lock rlock = rwlock.readLock();
private final Lock wlock = rwlock.writeLock();
StampedLock
读写锁(乐观读锁)
特点
相较于ReadWriteLock
读的过程中也允许获取写锁后写入
基本使用
类内部字段
private final StampedLock stampedLock = new StampedLock();
类的方法中:
long stamp = stampedLock.tryOptimisticRead(); // 获得一个乐观读锁
典型类的读代码
需要查验锁的版本号是否一致
Concurrent集合
描述:
Java标准库的java.util.concurrent包提供的线程安全的集合(并发集合类)
常见的集合对应的线程安全集合
使用:
与使用非线程安全的集合类完全相同
Map<String, String> map = new ConcurrentHashMap<>();
// 在不同的线程读写:
map.put("A", "1");
map.put("B", "2");
map.get("A", "1");
Atomic包
描述
提供了一组原子操作的封装类
位于java.util.concurrent.atomic包
线程池
特点
接收大量小任务并进行分发处理
使用
普通线程池
接口:ExecutorService
类:Executors
创建线程池
(类的静态方法)
FixedThreadPool:线程数固定的线程池;
CachedThreadPool:线程数根据任务动态调整的线程池;
SingleThreadExecutor:仅单线程执行的线程池。
向线程池中提交
例如线程Task
es.submit(new Task("" + i));
创建定时反复线程池
ScheduledThreadPool
接口:ScheduledExecutorService
类:Executors
创建线程池
(类的方法)
ScheduledExecutorService ses = Executors.newScheduledThreadPool(4);
向线程池中提交
(三种定时方式)
ses.schedule()
执行一次
ses.scheduleAtFixedRate()
固定时间间隔触发
ses.scheduleWithFixedDelay()
上一次任务执行完毕后,
等待固定的时间间隔,
再执行下一次任务
向线程池中提交任务
Runnable任务
提交的任务只需要实现Runnable接口,
就可以submit给线程池
问题:此接口没有返回值,只能保存到类的内部变量,提供额外的方法进行读取
Callable任务
Callable接口是一个泛型接口,
可以返回指定类型的结果
获得异步执行的结果
ExecutorService executor = Executors.newFixedThreadPool(4);
// 定义任务:(给出任务的返回值类型)
Callable<String> task = new Task();
// 提交任务并获得Future对象:
Future<String> future = executor.submit(task);
// 从Future获取异步执行返回的结果:
String result = future.get(); // 可能阻塞
Future对象的方法
get():获取结果(可能会等待)
get(long timeout, TimeUnit unit):
获取结果,但只等待指定的时间;
cancel(boolean mayInterruptIfRunning):
取消当前任务;
isDone():判断任务是否已完成。
0 条评论
下一页