多线程基础知识

概念关系

线程 —> 进程 —> 程序

简单来说,一个进程由多个线程组成,进程是一个程序的一次执行过程

一般Java程序默认会有main主线程以及gc线程(垃圾回收),线程的执行顺序是由CPU来决定的

创建线程的四种方式

  • 继承Thread类

    线程类继承Thread类之后重写run方法,主类创建线程对象,再调用线程对象的start方法,启动线程,如果调用的是线程的run方法是直接执行该线程,如果调用start方法,是与主线程交替执行。

  • 实现Runnable接口

    线程类实现Runnable接口,重写run方法,主类创建Thread线程对象,实现Runnable接口的对象作为参数传递,再调用start方法,方便同一个对象被多个线程使用

    多个线程同时使用会出现并发问题,线程不安全。

  • 实现Callable接口

    需要返回值类型,不是重写run方法,而是call方法,需要抛出异常

    创建线程对象后需要创建执行服务ExecutorService对象,再执行提交,用服务对象的commit方法,需要传入线程对象作为参数,再通过get方法获取结果,最后执行shotdownNow方法关闭服务。

  • 使用线程池

Lambda表达式

使用Lambda表达式可以避免内部类定义过多,代码看起来简洁,去掉一堆没有意义的代码,只留下核心的逻辑代码。

函数式接口:如果在一个接口中,只包含唯一一个抽象方法,就称之为函数式接口,可以通过lambda表达式来创建该接口的对象。

写法:在创建函数式接口时,不用再使用new关键字了,而是用 ()->{} 类似于箭头函数

可以简写,在只能有一行代码的情况下才能对其简化成一行,多行就用代码块包裹,可以去掉参数类型,在只有一个参数的情况下也可以去掉括号。

线程的状态

线程有五种状态:创建、就绪、运行、阻塞和死亡

流程图如下:

线程中有几个比较常用的方法

线程休眠:sleep() 使用sleep方法可以做网络延迟,放大问题的发生性

线程礼让:yield() 让当前正在执行的线程暂停,不阻塞,会让CPU重新调度,将线程从运行状态转为就绪状态。

线程插队:join() 让当前正在执行的线程停止,待到本线程执行完之后,再执行其他线程,其他线程会进到阻塞状态,相当于插队

停止线程可以用变量、循环来控制,线程只能启动一次,死亡后就不能再次启动了

线程的优先级

涉及到权重问题,默认的优先级是5,可以使用getPriority()获取优先级,setPriority(int xxx) 来改变优先级,优先级的设定在start()调度前,优先级低只是意味着获得调度的概率变低了,还是会被调用的。

守护线程(daemon)

线程分为用户现场和守护线程,虚拟机必须保证用户现场执行完毕,但又不用保证守护线程,也就是说,当用户线程执行完毕,主线程会结束,只要把Thread类的setDaemon设置为ture,就可以把该线程设置为守护线程。所有线程的getDaemon值默认是false,都是用户线程。

线程同步安全

并发:同一个对象被多个线程同时操作。

要保证线程同步安全,可以使用队列+锁(synchronized)

当一个线程获得对象的锁,独占资源,其他线程必须等待,使用完后再释放锁

但是会存在以下问题:

  • 一个线程持有锁会导致其他需要此锁的线程挂起
  • 性能问题,如果优先级高的线程等待优先级低的线程释放锁,会导致优先级倒置

同步方法和同步块

同步方法需要加synchronized 修饰方法,锁的是this

同步块需要用synchronized代码块包起来,需要传入锁的对象,这个对象一般是需要进行增删改操作的对象

死锁

两个线程各自等待对方释放资源,需要对方的锁,两个线程都会停止执行,当某一个同步块同时拥有两个以上对象的锁,可能会发生死锁问题。

达成死锁的条件:

  • 互斥条件:一个资源每次只能被一个线程使用

  • 请求与保持条件:一个线程因请求资源而阻塞,对获得的资源保持不放,即不会释放

  • 不剥夺条件:线程以获得的资源,在未使用完之前不能强行剥夺

  • 循环等待条件:若干线程之间形成一种头尾相连 的循环等待资源关系。

只要上诉条件中打破任意一个,就可以避免死锁的发生。

Lock锁

Lock是显式锁,需要手动关闭,使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。

使用顺序:Lock > 同步代码块 > 同步方法

线程通信

生产消费问题就是一个线程同步问题,生产者与消费者共享一个资源,两者相互依赖,互为条件,synchronized可以阻止并发更新同一个资源,实现了同步,不能同来实现不同线程之间的消息传递。

wait() 表示线程一直在等待,直到其他线程通知,会释放锁

notify() 唤醒一个处于等待状态的线程

notifyAll() 唤醒同一个对象上所以调用wait()的线程

三个方法都只能在同步代码块或同步方法中使用,否则会抛异常

解决方法——管理法

利用缓冲区,生产者将生产好的数据放入缓冲区,消费者直接从缓冲区拿数据

线程池

可以提高性能,提前创建多个线程放入线程池,使用时可直接获取,用完再放回线程池中,不用再手动销毁。

好处:

  • 提高响应速度,减少创建新线程的时间
  • 降低资源消耗,可重复利用
  • 便于线程管理
  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值