多线程笔记
文章目录
- 一、多线程概述
- 1.线程与进程
- 1.1线程(Thread)
- 1.2进程(Process)
- 2.线程调度
- 2.1分时调度
- 2.2抢占式调度(Java使用)
- 3.同步与异步
- 4.并发与并行
- 5.守护线程和用户线程
- 二、Java中的多线程
- 1.如何实现多线程
- 1.1继承Thread类
- 1.2 实现Runnable接口
- 1.3实现Runnable与继承Thread相比的优点
- 1.4带返回值的线程Callable
- 1.5Runnable与Callable相比
- 2.线程安全问题
- 2.1同步代码块(隐式锁)
- 2.2同步方法(隐式锁)
- 2.3显式锁Lock
- 3.公平锁与非公平锁
- 4.多线程通信问题
- 5.线程的六种状态
- 三、线程池
- 1.线程池概述
- 2.缓存线程池
- 3.定长线程池
- 4.单线程线程池
- 5.周期定长线程池
- 四、Lambda表达式
- 总结
一、多线程概述
1.线程与进程
1.1线程(Thread)
- 线程是CPU调度的最小单位
- 是进程中的一个执行路径,一个进程启动后,里面的若干执行路径又可以被划分为若干个线程
- 一个进程中至少有一个线程
- 同一个进程中的多个线程可以共享部分内存(方法区,堆),每个线程之间有些内存又是独立的(栈:虚拟机栈,本地方法栈,程序计数器)
1.2进程(Process)
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,进程与进程之间是无法直接共享内存的
- 每个进程之间是独立的,操作系统分配资源时,是以进程为单位的
- 两个进程之间进行切换,通信(交换数据)等操作时,成本比较高
- 进程是线程的容器,进程中可容纳若干个线程。是程序的实体,程序是死的,静态的,进程是正在执行的。
2.线程调度
- CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对于我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但能提高程序运行效率,让CPU的使用率更高。
2.1分时调度
所有线程轮流使用CPU的使用权,平均分配给每个线程占用CPU的时间
2.2抢占式调度(Java使用)
- 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)
3.同步与异步
同步和异步通常用来形容一次方法的调用
- 同步:排队执行,效率低但安全。同步调用一旦开始,调用者必须等到方法调用返回后,才能继续执行。
- 异步:同时执行,效率高但数据不安全。一旦开始,方法调用会立即返回,调用者可以继续后续工作,异步方法通常在另一个线程中真实执行,对于调用者来说似乎是瞬间完成了。
4.并发与并行
- 并发:指两个或多个事件在同一个时间段内发生
- 并行:指两个或多个事件在同一时刻发生(同时发生),真正意义上的同时执行
5.守护线程和用户线程
- 用户线程:当一个进程不包含任何存活的用户线程时,进程结束
守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡
public static void main(String[] args){
Thread t=new Thread();
t.setDaemon(true);//在启动线程前设置该线程为守护线程,当该进程中的用户线程都结束后,守护线程自动死亡
t.start();
}
二、Java中的多线程
1.如何实现多线程
1.1继承Thread类
public class Demo{
public static void main(String[] args){
MyThread my=new MyThread();
my.start();//启动线程,此时run和main是并发执行的
}
}
public class MyThread extends Thread{
@Override
public void run(){
//支线线程要执行的任务
}
}
1.2 实现Runnable接口
public class Demo{
public static void main(String[] args){
//1.创建一个任务
MyRunnable my=new MyRunnable();
//2.创建一个线程并给他分配一个任务
Thread t=new Thread(my);
t.start();
}
}
public class MyRunnable implements Runnable{
@Override
public void run(){
//支线线程要执行的任务
}
}
1.3实现Runnable与继承Thread相比的优点
1. 通过创建任务,然后给线程分配任务的方式来实现多线程,更适合多个线程同时执行相同任务的情况
2. 可以避免单继承带来的局限性
3. 任务与线程本身是分离的,提高了程序的健壮性
4. 线程池技术接受Runnable类型的任务,不接受Thread类型的线程
1.4带返回值的线程Callable
public static void main(String[] args){
Callable<?> my=new MyCallable();
FutureTask<?> future=new FutureTask<>(my);
new Thread(future).start();
//task.get();此时主线程会停下来等子线程的返回值
//task.isDone();判断该线程任务是否执行完毕
}
Class MyCallable implements Callable<T>{
@Override
public <T> call() throws Exception{
return T;
}
}
1.5Runnable与Callable相比
- 相同点:
- 都是接口
- 都可以编写多线程程序
都采用Thread.start()启动线程
- 不同点:
Runnable没有返回值;Callable可以返回执行结果
- Callable接口的call()允许抛出异常;Runnable的run()不能抛出
2.线程安全问题
2.1同步代码块(隐式锁)
- 多个线程应持同一个锁对象才有效
线程同步可以解决线程安全问题,但会降低效率(排队减速)
synchronized(锁对象){
//可能发生线程安全问题的代码
}
2.2同步方法(隐式锁)
- 非静态方法,同步方法的锁是this
如果是静态修饰的,那么锁对象是类名.class
//将需要同步的部分抽成一个方法
权限修饰符 synchronized 返回值类型 方法名(){//可能发生线程安全问题的代码
}
2.3显式锁Lock
//Lock子类ReentrantLock
private Lock l=new ReentrantLock();
l.lock();
/*要加锁的代码*/
l.unlock;
3.公平锁与非公平锁
公平锁:有先来后到的顺序
//fair默认是false(不公平锁),当传入参数是true时,为公平锁
Lock l=new ReentrantLock(boolean fair);非公平锁:线程一起抢,谁抢到锁算谁的。
4.多线程通信问题
//生产者与消费者问题
public class Demo {
public static void main(String[] args) {
Plant p=new Plant();
new Waiter(p).start();
new Cook(p).start();
}
}
class Cook extends Thread{
Plant p;
public Cook(Plant p) {
this.p = p;
}
@Override
public void run() {
while(true){
synchronized (p){
//假设平台上一次最多可以放十盘菜
if(p.getCount()>=10){
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
p.setCount(p.getCount()+1);
System.out.println("厨师生产了一盘菜,平台上还有:"+p.getCount()+"盘菜");
p.notify();
}
}
}
}
class Waiter extends Thread{
Plant p;
public Waiter(Plant p) {
this.p = p;
}
@Override
public void run() {
while(true){
synchronized (p){
if(p.getCount()<=0){
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
p.setCount(p.getCount()-1);
System.out.println("服务员取走了一盘菜,平台上还有:"+p.getCount()+"盘菜");
p.notify();
}
}
}
}
class Plant{
public int count=0;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
5.线程的六种状态
- NEW 线程刚被创建,但尚未启动
- RUNNABLE 正在执行的线程
- BLOCKED 被阻塞等待监视器锁定的线程
- WAITING 休眠的线程
- TIMED WAITING 有限期的休眠线程
- TERMINATED 死亡
三、线程池
1.线程池概述
- 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
- 线程池的好处
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
2.缓存线程池
长度无限制
//1.判断线程池是否存在空闲线程
//2.存在则使用
//3.不存在,则创建线程 并放入池中,然后使用
public class Demo {public static void main(String[] args) {
ExecutorService service= Executors.newCachedThreadPool();
service.execute(new Runnable(){
@Override
public void run(){
//任务代码
}
});
}
}
3.定长线程池
长度是指定的数值
/ 1.判断线程池是否存在空闲线程 2.存在则使用 3.不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用 4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 /
public class Demo {public static void main(String[] args) {
ExecutorService service= Executors.newFixedThreadPool(nThread);
service.execute(new Runnable(){
@Override
public void run(){
//任务代码
}
});
}
}
4.单线程线程池
/* 1.判断线程池的那个线程是否空闲 2.空闲则使用 3.不空闲,则等待池中的单个线程空闲后使用 */
public class Demo {
public static void main(String[] args) {
ExecutorService service= Executors.newSingleThreadExecutor();
service.execute(new Runnable(){
@Override
public void run(){
//任务代码
}
});
}
}
5.周期定长线程池
/* 周期性任务执行时:定时执行,当某个时机触发时,自动执行某任务 1.判断线程池是否存在空闲线程 2.存在则使用 3.不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用 4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 */
public class Demo {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(corePoolSize);
/** * 1.定时执行一次 * 参数1.定时执行的任务 * 参数2.时长数字 * 参数3.时长数字的时间单位,TimeUnit常量指定 */
service.schedule(new Runnable(){
@Override
public void run(){
//任务代码
}
},5,TimeUnit.SECONDS);
/** * 2.周期性执行任务 * 参数1.任务 * 参数2.延迟时长数字(第一次执行在什么时间后) * 参数3.周期时长数字(每隔多久执行一次) * 参数4.时长数字的时间单位,TimeUnit常量指定 */
service.scheduleAtFixedRate(new Runnable(){
@Override
public void run(){
//任务代码
}
},5,1,TimeUnit.SECONDS);
}
}
四、Lambda表达式
函数式编程思想
//想要实现Lambda表达式,接口中只能包含一个方法
public class Demo {public static void main(String[] args) {
/*冗余的做法 print(new MyMath() { @Override public int sum(int x, int y) { return x+y; } },1,2);*/
print((int x,int y)->{
return x+y;
},1,2);
}
public static void print(MyMath m,int x,int y){
int num=m.sum(x,y);
System.out.println(num);
}
static interface MyMath{
int sum(int x,int y);
}
}
还没有评论,来说两句吧...