Java多线程

1、多线程技术需要硬件支持
2、多线程技术就是同时执行多个应用程序

一、线程与进程

进程
正在运行的软件,每个进程都有一个独立的内存空间(堆、栈互不共享)
独立性:进程是一个能独立运行的基本单位同时也是系统分配资源和调度的独立单位
动态性:进程的实质是一次程序的执行过程,进程是动态产生,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行
线程
1.是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发 执行。
2. 一个进程最少有一个线程
3. 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干 执行路径又可以划分成若干个线程
即:进程就是一个操作系统中正在运行的应用程序,而线程就是应用程序中做的事情。

二、线程调度

分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
1.优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一 个(线程随机性),Java使用的为抢占式调度。
2.CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核 新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相 对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能 提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

三、同步与异步

同步:排队执行 , 效率低但是安全.
异步:同时执行 , 效率高但是数据不安全.

同步:同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
异步:异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。

四、线程优先级在这里插入图片描述

优先级的范围为1~10,默认为5
优先级越高的线程抢占CPU的几率越高

五、并发与并行

并发:同一时刻,有多个指令在单个CPU上交替执行(指两个或多个事件在同一个时间段内发生)。
并行:同一时刻,有多个指令在多个CPU上同时执行(指两个或多个事件在同一时刻发生(同时发生))。

六、继承Thread

1.每个线程都拥有自己的栈空间,共用一份堆内存
2.run方法就是线程要执行的任务方法,重写是因为run()是用来封装被线程执行的代码
3.新路径的执行方法不是调用run方法,而是通过thread对象的start()来启动任务
4.run():封装线程执行的代码,直接调用相当于普通的方法调用,并未开启线程
5.start():启动线程,然后由JVM来调用此线程的run()方法

getName():获取线程名称
setName():修改线程名称

获得线程对象:
Thread.currentThread().getName():获得当前线程名称

线程休眠:
Thread.sleep(1000):让线程休眠1秒

七、实现Runnable

1、创建一个任务对象
2、创建一个线程,并为其分配一个任务
3、执行这个线程

相比于继承Thread的优势:
(1)通过创建任务,然后给线程分配的方式来实现的多线程更适合多个线程同时执行任务的情况
(2)可以避免单继承带来的局限性(java没有多继承)
(3)任务与线程本身是分离的,提高了程序的健壮性
(4)线程池技术不接收Thread类型的线程

线程阻塞:消耗时间的操作(如:文件读取等)

八、线程的中断

.interrupt():中断标记
线程识别中断标记后会将产生的异常传入catch块,然后由程序员决定是否中断。
如果需要中断线程,使用return;即可,stop会直接杀死进程

九、守护线程

线程分为守护线程和用户线程
1.用户线程:当一个进程不包含任何存活的用户线程时,进行结束。
2.守护线程:守护用户线程的,当最后一个用户线程结束时,所有的守护线程自动死亡(由于CPU效率极高,守护线程也可能再运行几下)。
在这里插入图片描述

在这里插入图片描述
设置守护线程一定要在线程运行之前

十、线程安全

1.同步代码块
所有程执行任务时用的必须是同一把锁
public void run() {
synchronized(锁名){
代码块
}
}

2.同步方法
在方法的权返回值前面加上synchronized
(如果不是静态方法, 锁是this,否则是类名.class)

3.显示锁Lock

创造一把锁
Lock 对象名 = new ReentrantLock();
(Lock 对象名 = new ReentrantLock(fair:true);(fair参数为true时为公平锁))
开始:对象名.lock();
结束:对象名.unlock();

公平锁:
多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。
优点:所有的线程都能得到资源,不会饿死在队列中。
缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。
非公平锁:
多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。
优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
缺点:可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

十一、线程死锁

产生死锁的原因:(1)竞争系统资源 (2)进程的推进顺序不当

产生死锁的必要条件:
互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。

解决死锁:
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测

十二、多线程通信

生产者与消费者交替运行,即:生产者运行时消费者休眠,生产者运行完毕后,唤醒消费者进行休眠
.notifyAll();:唤醒正在等待对象监视器的所有线程
.notify();:唤醒正在等待对象监视器的单个线程
.wait();:进入等待(不唤醒就死等)
使用什么对象当锁就必须使用这个对象去调用以上方法

十三、线程的六种状态

1.初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. 阻塞(BLOCKED):表示线程阻塞于锁。
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6. 终止(TERMINATED):表示该线程已经执行完毕。
在这里插入图片描述

十四、Callable

可以和主线程并发执行,也可以先执行完后将值给主线程,之后主线程继续执行

  1. 编写类实现Callable接口 , 实现call方法
    class XXX implements Callable {
    @Override
    public call() throws Exception {
    return T;
    }
    }
  2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
    FutureTask future = new FutureTask<>(callable);
  3. 通过Thread,启动线程
    new Thread(future).start();

Runnable 与 Callable的相同点 :
1、都是接口
2、都可以编写多线程程序
3、都采用Thread.start()启动线程

Runnable 与 Callable的不同点 :
1、Runnable没有返回值;Callable可以返回执行结果
2、Callable接口的call()允许抛出异常;Runnable的run()不能抛出

Callable获取返回值 :
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

十五、线程池

线程池的好处
1、降低资源消耗。
2、提高响应速度。
3、提高线程的可管理性。

Java中的线程池共有四种:
1、缓存线程池 (长度无限制):
ExecutorService service = Executors.newCachedThreadPool();
(1) 判断线程池是否存在空闲线程
(2) 存在则使用
(3) 不存在,则创建线程 并放入线程池, 然后使用

2、定长线程池(长度是指定的数值):
ExecutorService service = Executors.newFixedThreadPool(2);
(1) 判断线程池是否存在空闲线程
(2) 存在则使用
(3) 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
(4) 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

3、单线程线程池:
ExecutorService service = Executors.newSingleThreadExecutor();
(1) 判断线程池的那个线程是否空闲
(2) 空闲则使用
(3) 不空闲,则等待池中的单个线程空闲后使用

4、周期性任务定长线程池 :
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
(1) 判断线程池是否存在空闲线程
(2) 存在则使用
(3) 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
(4) 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

周期性任务执行时:
定时执行, 当某个时机触发时, 自动执行某任务
/**

  • 定时执行
  • 参数1. runnable类型的任务
  • 参数2. 时长数字
  • 参数3. 时长数字的单位
    */
    /service.schedule(new Runnable() {
    @Override
    public void run() {
    System.out.println(“后排靠窗,王的故乡”);
    }
    },5,TimeUnit.SECONDS);
    /
    /
  • 周期执行
  • 参数1. runnable类型的任务
  • 参数2. 时长数字(延迟执行的时长)
  • 参数3. 周期时长(每次执行的间隔时间)
  • 参数4. 时长数字的单位
    */
    service.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
    System.out.println(“后排靠窗,王的故乡”);
    }
    },5,2,TimeUnit.SECONDS);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值