前言 :
本篇文章主要是用基于这篇文章的知识点摘要,对于我本人来说就是为了以后记忆模糊的时候做提示作用。
基础部分
一、线程的五个状态:
注:
(1):里面有三种阻塞Block,分别对应同步阻塞(synchronized修饰,未拿到同步锁),等待阻塞(调用了Object的wait()),其他阻塞(调用sleep(),join(),I/O请求)。
(2):从阻塞状态解除只能回到Runnable状态,也是就说到就绪态,只有等再次被分配Cpu时间时候才会转为Running态,同时其中等待阻塞只用被唤醒(notify(),notifyall())之后,才能便为同步阻塞,拿到同步锁之后才能变为就绪态。
二、线程的实现:
- 线程常见的只用两种方法:
(1):实现Runnable接口。(不过还是需要通过new Thread(接口的实现类的对象)来实现线程)
(2) : 继承Thread类(这个类其实也实现了Runnable接口)
注:因为可以实现多个接口,提高扩展性,推荐使用实现Runnable接口 - Thread和Runnable的异同
(1):相同:都能实现了线程
(2):不同:1.Thread是类,Runnable是接口 2.通过使用实现接口的类来创建的线程,可以共享这个类里面的资源(属性)。 - Runnable和Thread相比优点有:
(1)由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以
(2)Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread并不是不可以,而是相比于Runnable来说,不太适合。
三、 run()和start()的区别:
- start(): 实质是调用底层的start0(),这个方法来启动一个线程。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
- run(): 被直接调用的话就相当于一个普通的成员方法。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码;
四、 sychronized:
- sychronized锁所面向的是当前
对象 - 他的三个规则其实也是对这个锁只面向对象的三个方面解释。

- sychronized代码块比sychronized方法更加高效
- 通过sychronized实现的实例锁和全局锁
五、wait(),notify,notifyall()
- 首先明白synchronized(this)和synchronized(obj)的区别
synchronized(this)是获得当前对象的锁,也就是说,其他线程在想拿到这个对象的锁就需要等待
synchronized(obj)是获得obj这个对象的锁,其他线程在想拿到这个锁就需要等待了,通过这两个拿锁的方式进行实现同步。 - wait()这个方法不管是哪个线程调用,停止等待的线程
必定是正在运行的线程(wait()必须在同步代码块中或者同步方法中调用,这就意味着当前持有锁的线程一定是运行线程) - 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()
- yield()是Thread的方法,作用是让出当前CPU时间,重新回到就绪态,然后CPU重新从就绪态中挑选一个线程运行。可能他还会被挑中。
- yield()并不会释放锁。
七、sleep()
- sleep()休眠指定时间,时间到了重新变为就绪态,同时不会释放锁。
八、join()
- join()是Thread的方法,所用是让调用这个方法的线程立刻获得CPU,而其他正在运行的线程被迫调用wait(0),开始等待,并释放它的锁。直到调用join()方法的线程运行结束,重新唤醒之前的线程。
- 简单说:谁调用了join(),谁就是老大,其他人要让出资源,等老大工作完,并且等老大发话了,其他人才能开始工作。
九、interrupt()
- interrupt()方法就是设置当前线程的中断标记设为true(仅此而已),但是当前线程处于阻塞态时,中断标记立刻变为false,并抛出InterruptedException 。
- interrupted和isInterrupted方法是返回当前线程的中断标记(interrupted不仅返回状态而且会把中断状态设置为false)
十、线程的优先级和守护队列
- 线程优先级分为1~10,10最高,但是并不是优先级高的就优先执行完。
- 优先级是集成的,当前优先级为5,它创建的第线程优先级也就是5。
- 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()。
10万+

被折叠的 条评论
为什么被折叠?



