Java多线程知识总结

前言 :

本篇文章主要是用基于这篇文章的知识点摘要,对于我本人来说就是为了以后记忆模糊的时候做提示作用。

基础部分

一、线程的五个状态

五态图注:
(1):里面有三种阻塞Block,分别对应同步阻塞(synchronized修饰,未拿到同步锁),等待阻塞(调用了Object的wait()),其他阻塞(调用sleep(),join(),I/O请求)。
(2):从阻塞状态解除只能回到Runnable状态,也是就说到就绪态,只有等再次被分配Cpu时间时候才会转为Running态,同时其中等待阻塞只用被唤醒(notify(),notifyall())之后,才能便为同步阻塞,拿到同步锁之后才能变为就绪态。

二、线程的实现:

  1. 线程常见的只用两种方法:
    (1):实现Runnable接口。(不过还是需要通过new Thread(接口的实现类的对象)来实现线程)
    (2) : 继承Thread类(这个类其实也实现了Runnable接口)
    注:因为可以实现多个接口,提高扩展性,推荐使用实现Runnable接口
  2. Thread和Runnable的异同
    (1):相同:都能实现了线程
    (2):不同:1.Thread是类,Runnable是接口 2.通过使用实现接口的类来创建的线程,可以共享这个类里面的资源(属性)。
  3. Runnable和Thread相比优点有:
    (1)由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以
    (2)Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread并不是不可以,而是相比于Runnable来说,不太适合。

三、 run()和start()的区别:

  1. start(): 实质是调用底层的start0(),这个方法来启动一个线程。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
  2. run(): 被直接调用的话就相当于一个普通的成员方法。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码;

四、 sychronized:

  1. sychronized锁所面向的是当前对象
  2. 他的三个规则其实也是对这个锁只面向对象的三个方面解释。
    三条规则
  3. sychronized代码块比sychronized方法更加高效
  4. 通过sychronized实现的实例锁和全局锁

五、wait(),notify,notifyall()

  1. 首先明白synchronized(this)和synchronized(obj)的区别
    synchronized(this)是获得当前对象的锁,也就是说,其他线程在想拿到这个对象的锁就需要等待
    synchronized(obj)是获得obj这个对象的锁,其他线程在想拿到这个锁就需要等待了,通过这两个拿锁的方式进行实现同步。
  2. wait()这个方法不管是哪个线程调用,停止等待的线程必定是正在运行的线程(wait()必须在同步代码块中或者同步方法中调用,这就意味着当前持有锁的线程一定是运行线程
  3. notify()可以唤醒在等待当前对象的锁的线程,这个notify()也是这个对象继承自Object的方法
    4.为什么 wait()和notify()是Object的方法而不是Thread的方法?
    (1) 假设他们是Thread的方法,也就是说一个wait()调用,正在运行的线程释放锁,开始等待。
    (2)需要调用notify()才能唤醒该线程,但是这个线程已经开始等待,所以无法进行任何操作了,所以它只能由其他线程进行唤醒。
    (3)其他线程的notify()跟这个等待线程没有任何关系,就算运行也只能唤醒自己,但是自己还在运行中,所以定义在Thread中的notify()方法废了。
    (4)跟这个线程有关系的只要他在等待的锁,所以只能由这个锁是由这个对象持有,只能通过这个对象来唤醒这个等待线程了,如果notify()定义在Thread中,这个对象也无法调用notify()。
    (5)wait(),和notify()是依赖于这个锁对象的,所以wait()和notify()定义在Object中。
    (6)总结:简单来说锁是对象级的,而不是线程级别的

六、yield()

  1. yield()是Thread的方法,作用是让出当前CPU时间,重新回到就绪态,然后CPU重新从就绪态中挑选一个线程运行。可能他还会被挑中。
  2. yield()并不会释放锁。

七、sleep()

  1. sleep()休眠指定时间,时间到了重新变为就绪态,同时不会释放锁。

八、join()

  1. join()是Thread的方法,所用是让调用这个方法的线程立刻获得CPU,而其他正在运行的线程被迫调用wait(0),开始等待,并释放它的锁。直到调用join()方法的线程运行结束,重新唤醒之前的线程。
  2. 简单说:谁调用了join(),谁就是老大,其他人要让出资源,等老大工作完,并且等老大发话了,其他人才能开始工作。

九、interrupt()

  1. interrupt()方法就是设置当前线程的中断标记设为true(仅此而已),但是当前线程处于阻塞态时,中断标记立刻变为false,并抛出InterruptedException 。
  2. interrupted和isInterrupted方法是返回当前线程的中断标记(interrupted不仅返回状态而且会把中断状态设置为false)

十、线程的优先级和守护队列

  1. 线程优先级分为1~10,10最高,但是并不是优先级高的就优先执行完。
  2. 优先级是集成的,当前优先级为5,它创建的第线程优先级也就是5。
  3. Java线程分为用户线程,守护线程(Daemon),当用户线程都结束,守护进程自动结束,JVM退出

JUC原子类

二、并发的三大特性

(1)原子性:一个操作可以当做一个整体一起执行,要么里面的所有操作都执行完毕,要么全部都不执行。
(2)可见性:就是当前数据修改以后,后面的所以操作拿到的值都是这个修改过后的值。或者就是这个修改是对后面的操作或者线程是可见的。
(3)有序性:就是按照我们编写的代码顺序进行执行。因为处理器会进行代码优化,就是可能会调代码的执行顺序,但是对最后的结果是没有影响的。
synchronized,lock可以保证三个特性,volitile可以保证一定的有序性,和可见性。

一、volatile 关键字(两个特性)

(1)保证变量的可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入,这个新值对于其他线程来说是立即可见的。
(2)屏蔽指令重排序:指令重排序是编译器和处理器为了高效对程序进行优化的手段。
这时候有一个名词叫做内存屏障,也就是说使用Volatile修饰,就是在这个指令前加一个内存屏障,在屏障前的代码比如在前面执行完,后面的代码必须在后面执行。同时在这个变量修改之后,内存屏障会强制更新一下内存。

二、stop()和interrupt()

(1) 一个线程在之前老的JDK中使用的是Thread.stop()方法,但是后面发现这种处理方法是很危险而且不安全的,由于stop()方法已经在JDK中被标明是“作废/过期”的方法,显然它在功能上是具有缺陷的。作为一个负责的java工程师,最好是不要去使用它,因为使用stop()方法释放锁将会给数据造成不一致性的结果。如果出现这种情况,程序处理的数据可能遭到破坏,最终导致程序执行流程错误。
(2) interrupt()其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行。

由于我们不建议直接停止线程,所以我们更加巧妙的设计停止线程。通过设置中断标志,来进行判断,使用异常、休眠、返回这三种来停止进程。

三、锁池和等待池

因为锁是依靠于对象的,所以这两个也是对于对象来说的。
(1)锁池就是,多个线程 请求锁,只有一个线程拿到了锁,其他线程会进入这个对象的锁池中等待,直到拿到锁的线程释放锁,在从锁池中选一个赋予锁。
(2)等待池,则是由于wait()方法,当调用wait()方法之后,持有锁的线程会释放锁,并且会进入等待池,直到持有对象的线程调用notify()。

线程池

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝多芬也爱敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值