我叫目录
** 作为自己复习和分享**
1.2019年4月份版本JUC(java.util.concurrent)
1.1进程/线程
进程:是一个程序运行的最小单位(巧记:先“进”来,然后才能看到线程)
线程:是进程里最小的运行单位(例如idea进程,里面有多个微服务在运行,就是线程)
1.2并发/并行
并发:
在相对的一段时间内,其实是很短,对计算机有感,对人无感,多个线程抢占资源来执行自
己 ,执行A一段时间,马上执行B,然后执行C,以此类推,这几个线程对人来说可以看成
是“同时”完成的,也即并发,但对机器来说就是时间错开运行的。
并行:
多个线程在相同的时间内同时运行。
1.3 三个包
java.util.concuurent
java.util.concuurent.atomic
java.util.concuurent.locks
1.4JUC强大的辅助类
1.4.1countDownLatch
package ticket;
import java.util.concurrent.CountDownLatch;
public class CountDown {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6 ; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "离开教室");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println("班长离开教室");
}
}
原理
** 1.4.2cyclicBarrie**
人到齐了再开会
package ticket;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CountDown {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 1; i <= 7 ; i++) {
final int tempInt = i;
new Thread(() -> {
System.out.println("第"+tempInt+ "颗龙珠");
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
** 1.4.3信号灯semaphore **
package ticket;
import java.util.concurrent.Semaphore;
public class CountDown {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6 ; i++) {
final int tem = i;
new Thread(() -> {
try {
semaphore.acquire();
System.out.println("第"+tem+"辆车停入车库");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第"+tem+"辆车离开车库");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
1.5 读写分离 ReentrantReadWriteLock
读读可共享,写读和写写中写独占
读读能共存,读写,写写都不能共存
2.售票多线程
题目:三个售票员,卖出 ,30张票
笔记:如何编写企业级的多线程代码
固定的编程套路+模板是什么?
1.在高内聚低耦合的前提下,线程 操作 资源类。
1.1 一言不合,先创建一个资源类
1.2 一个线程start之后不是马上就启动,调度跟底层的cup有关系
1.3多线程有哪几种状态Thread.State看api即可,有六种
NEW、RUNNABLE、BLOCKED、WAITING(死等)、TIMED_WAITING(过时不候)、TERMINATED
class Ticket{ //资源类 = 实例变量+实例方法
private int number = 120;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock();
try {
if(number > 0) {
System.out.println(Thread.currentThread().getName()+"\t卖出第:"+(number--)+"\t还剩:"+number);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class SaleTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
/*new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <=40 ; i++) {
ticket.sale();
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <=40 ; i++) {
ticket.sale();
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <=40 ; i++) {
ticket.sale();
}
}
}, "C").start();*/
//使用lambda表达式之后只用三行代码
new Thread( () -> {for (int i = 0; i <=40 ; i++) ticket.sale();} ,"A").start();
new Thread( () -> {for (int i = 0; i <=40 ; i++) ticket.sale();} ,"B").start();
new Thread( () -> {for (int i = 0; i <=40 ; i++) ticket.sale();} ,"C").start();
}
3.lambda表达式简单复习
3.1 函数式编程
3.1.1拷贝小括号 , 中间右箭头,落地大括号
3.1.2
@FunctionalInterface(使用这个注解的接口一定是函数式接口)
函数式接口里可以有除了唯一方法之外的默认方法
静态方法
default static
package lambda;
@FunctionalInterface
interface Foo {
// public void sayHello();
int add(int x, int y);
default int plus(int x, int y){
return x + y;
}
public static int div (int x, int y){
return x/y;
}
}
public class LambdaTest {
public static void main(String[] args) {
// Foo foo = () -> { System.out.println("lambda函数式编程");};
Foo foo = (int x , int y) -> {
System.out.println("两数之和");
return x + y;
};
System.out.println(foo.add(6, 9));
}
}
4.集合类不安全
4.1 故障现象(并发修改异常)
java.util.ConcurrentModificationException
4.2导致原因
多线程并发,资源未加锁
4.3解决方法
1.Vector是线程安全的,加了锁
2.Collections.synchronizedList(new ArrayList());
3.new CopyOnWriteArrayList(); (写时复制)
4.4优化建议(同样的错误不犯第二次)
高并发下推荐使用juc里的
4.5常用集合相对的安全的集合
HashMap -> ConcurrentHashMap
HashSet -> CopyOnWriteArraySet
ArrayList -> CopyOnWriteArrayList
5.八锁原理讲解
以手机发短信和邮件为场景
1.标准访问,先打印邮件还是短信?
先邮件后短信
静态方法锁的是类,普通方法锁的是对象
phone里的锁,会把这个对象给锁住,只要有一个线程进来访问,那么其他线程就都进不来。
class Phone{
public synchronized void sendEmainl()throws Exception{
System.out.println("sendEmail");
}
public synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
- 暂停4秒钟邮件方法,请问先打印邮件还是短信?
先邮件后短信
class Phone{
public synchronized void sendEmainl()throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
public synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
3.新增普通sayHello方法,请问先打印邮件还是hello?
先hello后邮件
普通方法不会和加锁的方法争抢资源,所以不会被锁
package basuo;
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmainl()throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
public synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
public void sayHello()throws Exception{
System.out.println("sayHello");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
// phone.sendSMS();
phone.sayHello();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
4.两部手机,先打印邮件还是短信?
先短信后邮件
package basuo;
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmainl()throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
public synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
public void sayHello()throws Exception{
System.out.println("sayHello");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone2.sendSMS();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
5.两个静态同步方法,先打印邮件还是短信?
先邮件后短信
对象锁和全局锁的区别,静态同步方法是全局锁,把Class锁了
package basuo;
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmainl()throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
public static synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
public void sayHello()throws Exception{
System.out.println("sayHello");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
6.两个静态同步方法,两部手机,先打印邮件还是短信?
先邮件后短信
package basuo;
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmainl()throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
public static synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
public void sayHello()throws Exception{
System.out.println("sayHello");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone2.sendSMS();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
7.一个静态同步方法,一个普通同步方法,同一部手机,结果?
先短信后邮件
package basuo;
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmainl()throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
public synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
public void sayHello()throws Exception{
System.out.println("sayHello");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
8.一个静态同步方法,一个普通同步方法,两部手机,结果?
先短信后邮件
package basuo;
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmainl()throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("sendEmail");
}
public synchronized void sendSMS()throws Exception{
System.out.println("sendSMS");
}
public void sayHello()throws Exception{
System.out.println("sayHello");
}
}
public class BaSuo {
public static void main(String[] args) throws Exception{
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendEmainl();
}catch (Exception e){
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone2.sendSMS();
}catch (Exception e){
e.printStackTrace();
}
},"B").start();
}
}
6生产者消费者
题目:现在两个线程,可以操作初始值为零的一个变量,实现一个线程对该变量加1,一个线程对该变量减1,实现交替,来10轮,变量初始值为零。
1. 高内聚低耦合前提下,线程操作资源类
2. 判断 执行 通知
3. 防止线程的虚假唤醒(多线程里,条件判断不可以用if要用while)
package prodconsumer;
class Aircondition{
private int number = 0;
public synchronized void increment() throws InterruptedException {
//1.判断(if要改成while就不会有虚假唤醒)
while (number != 0) {
this.wait();
}
//2.干活
number ++ ;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3.通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//1.判断
while (number == 0) {
this.wait();
}
//2.干活
number -- ;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3.通知
this.notifyAll();
}
}
public class ProdConsumer {
public static void main(String[] args) {
Aircondition aircondition = new Aircondition();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"D").start();
}
}
生产者消费者2
package prodconsumer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Aircondition{
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
//1.判断
while (number != 0) {
condition.await();
}
//2.干活
number ++ ;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3.通知
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
//1.判断
while (number == 0) {
condition.await();
}
//2.干活
number -- ;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3.通知
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ProdConsumer {
public static void main(String[] args) {
Aircondition aircondition = new Aircondition();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() -> {
for (int i = 0; i <10 ; i++) {
try {
aircondition.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"D").start();
}
}
7.condition例子
题目:多线程之间按顺序调用,实现A->B->C
三个线程启动,要求如下:
A打印5次,B打印10次,c打印15次
接着
A打印5次,B打印10次,c打印15次
循环10轮
package prodconsumer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//资源类
class ConditionDemo1{
private int number = 1; //A :1 B :2 C :3
private Lock lock = new ReentrantLock();
private java.util.concurrent.locks.Condition condition1 = lock.newCondition();
private java.util.concurrent.locks.Condition condition2 = lock.newCondition();
private java.util.concurrent.locks.Condition condition3 = lock.newCondition();
public void print5(){
lock.lock();
try {
// 1.判断
while (number != 1){
condition1.await();
}
// 2.执行
for (int i = 0; i <5 ; i++) {
System.out.println(Thread.currentThread().getName() + "\t:" + i);
}
// 3.通知
number = 2;
condition2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void print10(){
lock.lock();
try {
// 1.判断
while (number != 2){
condition2.await();
}
// 2.执行
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName() + "\t:" + i);
}
// 3.通知
number = 3;
condition3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void print15(){
lock.lock();
try {
// 1.判断
while (number != 3){
condition3.await();
}
// 2.执行
for (int i = 0; i <15 ; i++) {
System.out.println(Thread.currentThread().getName() + "\t:" + i);
}
// 3.通知
number = 1;
condition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class Condition {
public static void main(String[] args) {
ConditionDemo1 conditionDemo1 = new ConditionDemo1();
new Thread( () -> {
for (int i = 0; i <10 ; i++) {
conditionDemo1.print5();
}
},"A").start();
new Thread( () -> {
for (int i = 0; i <10 ; i++) {
conditionDemo1.print10();
}
},"B").start();
new Thread( () -> {
for (int i = 0; i <10 ; i++) {
conditionDemo1.print15();
}
},"C").start();
}
}
8.callabe
实现线程的第三种方法
package prodconsumer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThred implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("******Mythread name call******");
return 1024;
}
}
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask(new MyThred());
new Thread(futureTask,"A").start();
Integer integer = futureTask.get();
System.out.println(integer);
}
}
1.get方法一般放在最后
9.JVM
9.1JVM架构图
9.2类加载器
9.2.1加载器类型
1.虚拟机自带的加载器
启动类加载器(Bootstrap)C++ (返回为null)
扩展类加载器(Extension)Java
应用程序加载器(AppClassLoader)也叫系统列加载器,加载当前应用的的classpath的所有类(返回为appcalssloader)
2.用户自定义加载器
Java.lang.ClassLoader的子类,用户可以定制类的加载方式
9.2.2双亲委派机制
一句话,我爸是李刚,有事找他(哈哈哈)-----------老师说
理论解释:
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在他的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。
双亲委派机制保证了沙箱安全。
9.3本地方法
native 放在本地方法栈(native method stack)
9.4PC寄存器
程序计数器(Program counter register)
记录了方法之间的调用和执行情况,类似排班值日表
可以理解成一个指针,指向下一个应该执行的程序
如果执行native方法,那么计数器为空
9.5方法区
供各线程共享的运行时内存区域。**他存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容。
上面讲的是规范,在不同虚拟机里面实现是不一样的,最典型的就是永久代(Permgen space)和元空间(Metaspace)
** 但是
实例变量存在堆内存中,和方法区无关
9.6java栈
栈管运行,堆管存储
程序 = 算法 + 数据结构
程序 = 框架 + 业务逻辑
9.6.1栈内存
主管java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就over,生命周期和线程一致,是线程私有的。
8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配
栈+堆+方法区的交互关系
9.7堆
- 堆、heap
- 1.新生代 NEW
- 1.伊甸区(Eden区)
在这个阶段GC,称为YGC或轻量GC - 2.幸存者0区(survivor 0 sapce)
- 3.幸存者1区 (survivor 1 space)
- 1.伊甸区(Eden区)
- 2.老年代 Old
这里满了之后开启full GC = FGC - 3.元空间
- 1.新生代 NEW
堆物理上分两部分:新生代+老年代
堆内存逻辑上分三部分:新生代+ 老年代+元空间
9.8对象的生命周期
幸存者0区也叫from区,幸存者1区也叫to区
form和to两个区位置和名称不是固定的,每次GC之后会交换,交换之后,谁是空的谁是to、
9.9堆参数调优入门
FullGC(这张图太长了,后边的缺失部分和GC一致)
9.10GC是什么(分代收集算法)
- 次数上频繁收集Yong区
- 次数上较少收集Old区
- 基本不动元空间
9.11GC收集算法
- GC和FullGC不是同时执行
- FullGC的运行一次是GC的10倍左右,因为fullGC清理的空间比GC要清理的空间大
- 四种算法
-
1.引用计数法
-
2.复制算法(Copying)
- 年轻代中使用的是Minor GC,这种算法采用的是复制算法
- 是什么
- 劣势
1.浪费了一般的内存
-
3.标记清除(Mark-Sweep)
- 老年代一般由标记清除或者标记清除与标记整理的混合实现
- 是什么
- 劣势
- 两次扫描,耗时严重
- 会产生内存碎片
-
4.标记压缩(Mark-Compact)(标记整理)
- 老年代一般是由标记清除或者是标记清除与标记整理的混合实现
- 是什么
- 劣势
标记整理算法唯一的缺点即使效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。
从效率上来说,标记整理算法要低于复制算法 - 标记清除压缩(Mark - Sweep - Compact)
-
9.11.1小总结
- 内存效率:复制算法>标记清除算法>标记整理算法(这里的效率只是简单的对比时间复杂度,实际情况不一定如此)
- 内存整齐度:复制算法=标记整理算法>标记清除算法
- 内存利用率:标记整理算法=标记清除算法>复制算法
9.11.2面试题
- 1.jvm内存模型以及分区,需要详细到每个分区放什么
- 2.堆里面的分区:Eden,survivalfrom to,老年代,各自的特点
- 3.GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方
- 4.Minor GC 与 Full GC分别在什么时候发生
10.JMM(Java 内存模型)