多线程学习(一)

进程、线程、协程

  • 进程:是程序运行和资源分配的基本单位。一个程序至少有一个进程,一个进程可以有多个线程、多个协程。
  • 线程:是进程的实体,是cup能够进行调度的最小单位。多个线程共享内存资源,减少切换次数,提高效率,多个线程之间可以并发执行。
  • 协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。

并行和并发

  • 并行是指两个或者多个事件在同一时刻发生;而并发是指两个或者多个事件在同一时间间隔发生。
  • 并行是在不同实体发生多个事件;而并发是在同一个实体发生多个事件。

创建线程方式

  1. 继承Thread类,重写run方法。
  2. 实现Runnable接口,定义接口实现类,重写run方法。
  3. 实现Callable接口和Future创建线程,定义接口实现类并实现call方法,使用FutureTask类包装Callable对象。使用FutureTask对象作为Thread对象的Target创建并启动新线程。
  4. 通过线程池创建线程。

Runnable和Callable区别

  • Runnable接口中的run()方法的返回值是void,只是单纯的去Run而已。
  • Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

线程状态

  • 创建状态
  • 就绪状态
  • 运行状态
  • 阻塞状态
  • 死亡状态

在这里插入图片描述
(此图片来源https://blog.csdn.net/pange1991/article/details/53860651)

sleep()和wait()区别

sleep():方法是线程类(Thread)的静态方法,让调用此方法的线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争Cup的执行时间。调用sleep()方法的对象的锁不会释放,开玩笑的说,sleep()方法是抱着锁睡觉的。

wait()方法:是Object类的方法,当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,与此同时会释放对象的锁,使其他线程能够进行访问,可以通过notify、notifyAll方法来唤醒等待的线程。

notify()和notifyAll()区别

  • notify()方法一次只能唤醒一个线程从等待池进入锁池。
  • notifyAll()方法一次会唤醒所有等待池中的线程进入锁池。

join()方法和yield()方法区别

  • join()方法:调用join()方法的线程会先执行,并且阻塞原先正在执行的线程,阻塞的线程要等调用join()方法的线程执行完才会继续执行。这就是“join(加入)”方法的作用。
  • yield()方法:和join()方法相反,调用yield()方法的线程会让出位置,给别的线程执行,等别的线程执行完了再执行调用yield()方法的线程。

创建线程池方式

  1. newFixedThreadPool(int n)

创建一个固定长度(n)的线程池,每提交一个任务就会创建一个线程,当线程池达到最大规模的时候,超出的线程会在队列等待;当线程发生未知的错误而结束时,线程池会补充新线程。

  1. newCachedThreadPool()

创建一个可缓存的线程池,如果线程池的规模超过处理需求,会回收空闲线程;否则会创建新线程,线程池的规模不存在任何限制。

  1. newSingleThreadExecutor()

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。

  1. newScheduledThreadPool(int n)

创建一个固定长度的线程池,而且以延时或定时的方式来执行任务。

线程安全三大原则

  • 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作 。
  • 可见性:一个线程对数据的修改可以及时地被其他线程看到。
  • 有序性:一个线程观察其他线程的指令执行顺序。

死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或彼此通信而造成一种阻塞的现象,若无外力的作用下,它们都将无法推进下去。此时称系统处于死锁状态,这些永远在等待的进程称为死锁进程。

死锁的四个必要条件:

  • 互斥条件:进程对所分配的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放资源。
  • 请求和保持条件:进程获得一定的资源后,又对其他资源发出请求,但是该资源可能被其他进程占有,此时请求阻塞,但又对自己获得的资源保持不放。
  • 不可剥夺条件:指进程获得资源后,在未完成使用之前,不可被剥夺,只能等自己使用完后释放。
  • 环路等待条件:指进程发送死锁后,若干进程之间形成一种头尾相接的循环等待资源的关系。

ThreadLocal

ThreadLocl是线程局部变量,属于线程自身所有,不在多个线程之间共享。ThreadLocal适用于每个线程需要自己的独立实例而且需要在多个方法中使用。

Synchronized

Synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法进入临界区,同时它还可以保证共享变量的内存可见性。

java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

  • 普通同步方法:锁是当前实例对象。
  • 静态同步方法:锁是当前类对象。
  • 同步方法块:锁是括号里面的对象。

synchronized和volatile的区别

volatile关键字的作用:保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。

  • volatile作用在变量级别;synchronize则可以使用在变量、方法和类级别上。
  • volatile仅能实现变量的修改可见性,但保证不了原子性;而synchronized可以保证变量修改的可见行以及原子性。
  • volatile不会造成线程阻塞;而synchronized会造成线程阻塞。
  • volatile标记的变量不会被编译器优化,synchronized标记的变量会被编译器优化。

synchronized和lock区别

  • synchronized是java的关键字;在jvm层面,Lock是个java类。
  • synchronized无法判断是否获取锁的状态;Lock可以判断是否获取到锁。
  • synchronized会自动释放锁;Lock需要在finally中手动释放锁(调用unlock()方法释放锁)。
  • synchronized适合少量代码同步问题;Lock适合大量代码同步问题。
  • synchronized锁可重入、不可中断、非公平;Lock锁可重入、可中断、可公平。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值