当前位置:网站首页 > Java基础 > 正文

java的基础思维导图



  • 14.5、自旋锁

话不多说,先上图。

并发基础

1、基本概念

========================================================================

欲说线程,必先说进程。

  • 进程:进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。

  • 线程:线程是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。

操作系统在分配资源时是把资源分配给进程的, 但是 CPU 资源比较特殊,它是被分配到线程的,因为真正要占用CPU运行的是线程,所以也说线程是 CPU分配的基本单位

在Java中,当我们启动 main 函数其实就启动了一个JVM进程,而 main 函数在的线程就是这个进程中的一个线程,也称主线程。

示意图如下:

程序进程线程关系

一个进程中有多个线程,多个线程共用进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈。

2、线程创建和运行

===========================================================================

Java中创建线程有三种方式,分别为继承Thread类、实现Runnable接口、实现Callable接口。

  • 继承Thread类,重写run()方法,调用start()方法启动线程

public class ThreadTest {

/**

  • 继承Thread类

*/

public static class MyThread extends Thread {

@Override

public void run() {

System.out.println(“This is child thread”);

}

}

public static void main(String[] args) {

MyThread thread = new MyThread();

thread.start();

}

}

  • 实现 Runnable 接口run()方法

public class RunnableTask implements Runnable {

public void run() {

System.out.println(“Runnable!”);

}

public static void main(String[] args) {

RunnableTask task = new RunnableTask();

new Thread(task).start();

}

}

上面两种都没有返回值。

  • 实现Callable接口call()方法,这种方式可以通过FutureTask获取任务执行的返回值

public class CallerTask implements Callable {

public String call() throws Exception {

return “Hello,i am running!”;

}

public static void main(String[] args) {

//创建异步任务

FutureTask task=new FutureTask(new CallerTask());

//启动线程

new Thread(task).start();

try {

//等待执行完成,并获取返回结果

String result=task.get();

System.out.println(result);

} catch (InterruptedException e) {

e.printStackTrace();

} catch (ExecutionException e) {

e.printStackTrace();

}

}

}

3、常用方法

========================================================================

3.1、线程等待与通知


在Object类中有一些函数可以用于线程的等待与通知。

  • wait():当一个线程调用一个共享变量的 wait()方法时, 该调用线程会被阻塞挂起, 到发生下面几件事情之一才返回 :(1) 线程调用了该共享对象 notify()或者 notifyAll()方法;(2)其他线程调用了该线程 interrupt() 方法,该线程抛出InterruptedException异常返回。

  • wait(long timeout) :该方法相 wait() 方法多了一个超时参数,它的不同之处在于,如果一个线程调用共享对象的该方法挂起后,没有在指定的 timeout ms时间内被其它线程调用该共享变量的notify()或者 notifyAll() 方法唤醒,那么该函数还是会因为超时而返回。

  • wait(long timeout, int nanos),其内部调用的是 wait(long timout)函数。

上面是线程等待的方法,而唤醒线程主要是下面两个方法:

  • notify() : 一个线程调用共享对象的 notify() 方法后,会唤醒一个在该共享变量上调用 wait 系列方法后被挂起的线程。 一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。

  • notifyAll() :不同于在共享变量上调用 notify() 函数会唤醒被阻塞到该共享变量上的一个线程,notifyAll()方法则会唤醒所有在该共享变量上由于调用 wait 系列方法而被挂起的线程。

如果有这样的场景,需要等待某几件事情完成后才能继续往下执行,比如多个线程加载资源,需要等待多个线程全部加载完毕再汇总处理。Thread类中有一个join方法可实现。

3.2、线程休眠


Thread类中有一个静态态的 sleep 方法,当一个个执行中的线程调用了Thread 的sleep方法后,调用线程会暂时让出指定时间的执行权,也就是在这期间不参与 CPU 的调度,但是该线程所拥有的监视器资源,比如锁还是持有不让出的。指定的睡眠时间到了后该函数会正常返回,线程就处于就绪状态,然后参与 CPU 的调度,获取到 CPU 资源后就可以继续运行。

3.3、让出优先权


Thread 有一个静态 yield 方法,当一个线程调用 yield 方法时,实际就是在暗示线程调度器当前线程请求让出自己的CPU 使用,但是线程调度器可以无条件忽略这个暗示。

当一个线程调用 yield 方法时, 当前线程会让出 CPU 使用权,然后处于就绪状态,线程调度器会从线程就绪队列里面获取一个线程优先级最高的线程,当然也有可能会调度到刚刚让出 CPU 的那个线程来获取 CPU 行权。

3.4、线程中断


Java 中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理。

  • void interrupt() :中断线程,例如,当线程A运行时,线程B可以调用钱程interrupt() 方法来设置线程的中断标志为 true 并立即返回。设置标志仅仅是设置标志, 线程A实际并没有被中断, 会继续往下执行。如果线程A因为调用了wait() 系列函数、 join 方法或者 sleep 方法阻塞挂起,这时候若线程 B调用线程A的interrupt()方法,线程A会在调用这些方法的地方抛出InterruptedException异常而返回。

  • boolean isInterrupted() 方法: 检测当前线程是否被中断。

  • boolean interrupted() 方法: 检测当前线程是否被中断,与 isInterrupted 不同的是,该方法如果发现当前线程被中断,则会清除中断标志。

4、线程状态

========================================================================

上面整理了线程的创建方式和一些常用方法,可以用线程的生命周期把这些方法串联起来。

在Java中,线程共有六种状态:

| 状态 | 说明 |

| — | — |

| NEW | 初始状态:线程被创建,但还没有调用start()方法 |

| RUNNABLE | 运行状态:Java线程将操作系统中的就绪和运行两种状态笼统的称作“运行” |

| BLOCKED | 阻塞状态:表示线程阻塞于锁 |

| WAITING | 等待状态:表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) |

| TIME_WAITING | 超时等待状态:该状态不同于 WAITIND,它是可以在指定的时间自行返回的 |

| TERMINATED | 终止状态:表示当前线程已经执行完毕 |

线程在自身的生命周期中, 并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换,Java线程状态变化如图示:

Java线程状态变化

5、线程上下文切换

===========================================================================

使用多线程的目的是为了充分利用CPU,但要认识到,每个CPU同一时刻只能被一个线程使用。

线程切换-2020-12-16-2107

为了让用户感觉多个线程是在同时执行的, CPU 资源的分配采用了时间片轮转也就是给每个线程分配一个时间片,线程在时间片内占用 CPU 执行任务。当线程使用完时间片后,就会处于就绪状态并让出 CPU 让其他线程占用,这就是上下文切换。

image-20210202172806362

6、线程死锁

========================================================================

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直相互等待而无法继续运行下去。

image-20210202173326028

那么为什么会产生死锁呢? 死锁的产生必须具备以下四个条件:

  • 互斥条件:指线程对己经获取到的资源进行它性使用,即该资源同时只由一个线程占用。如果此时还有其它线程请求获取获取该资源,则请求者只能等待,直至占有资源的线程释放该资源。

  • 请求并持有条件:指一个 线程己经持有了至少一个资源,但又提出了新的资源请求,而新资源己被其它线程占有,所以当前线程会被阻塞,但阻塞 的同时并不释放自己已经获取的资源。

  • 不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其它线程抢占,只有在自己使用完毕后才由自己释放该资源。

  • 环路等待条件:指在发生死锁时,必然存在一个线程——资源的环形链,即线程集合 {T0,T1,T2,…… ,Tn} 中 T0 正在等待一 T1 占用的资源,Tl1正在等待 T2用的资源,…… Tn 在等待己被 T0占用的资源。

该如何避免死锁呢?答案是至少破坏死锁发生的一个条件

其中,互斥这个条件我们没有办法破坏,因为用锁为的就是互斥。不过其他三个条件都是有办法破坏掉的,到底如何做呢?

  • 对于“请求并持有”这个条件,可以一次性请求所有的资源。

  • 对于“不可剥夺”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。

  • 对于“环路等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后就不存在环路了。

7、线程分类

========================================================================

Java中的线程分为两类,分别为 **daemon 线程(守护线程)**和 user 线程(用户线程)

在JVM 启动时会调用 main 函数,main函数所在的钱程就是一个用户线程。其实在 JVM 内部同时还启动了很多守护线程, 比如垃圾回收线程。

那么守护线程和用户线程有什么区别呢?区别之一是当最后一个非守护线程束时, JVM会正常退出,而不管当前是否存在守护线程,也就是说守护线程是否结束并不影响 JVM退出。换而言之,只要有一个用户线程还没结束,正常情况下JVM就不会退出。

8、ThreadLocal

===============================================================================

ThreadLocal是JDK 包提供的,它提供了线程本地变量,也就是如果你创建了ThreadLocal ,那么访问这个变量的每个线程都会有这个变量的一个本地副本,当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。创建 ThreadLocal 变量后,每个线程都会复制 到自己的本地内存。

image-20210202182241538

可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。

下面来看一个ThreadLocal的使用实例:

public class ThreadLocalTest {

//创建ThreadLocal变量

static ThreadLocal localVar java的基础思维导图 = new ThreadLocal();

//打印函数

static void print(String str) {

//打印当前线程本地内存中localVar变量值

System.out.println(str + “:” + localVar.get());

//清除前线程本地内存中localVar变量值

//localVar.remove();

}

public static void main(String[] args) {

Thread thread1 = new Thread(new Runnable() {

public void run() {

//设置线程1中本地变量localVal的值

localVar.set(“线程1的值”);

//调用打印函数

print(“线程1”);

//打印本地变量的值

System.out.println(“线程1打印本地变量后:” + localVar.get());

}

});

Thread thread2 = new Thread(new Runnable() {

public void run() {

//设置线程2中本地变量localVal的值

localVar.set(“线程2的值”);

//调用打印函数

print(“线程2”);

//打印本地变量的值

System.out.println(“线程2打印本地变量后:” + localVar.get());

}

});

thread1.start();

thread2.start();

}

}

9、Java内存模型

============================================================================

在Java中,所有实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享 。

Java线程之间的通信由Java内存模型控制,Java内存模型决定一个线程对共享变量的写入何时对另一个线程可见。

从抽象的角度来看,Java内存模型定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是Java内存模型的 一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

Java内存模型的抽象示意如图:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

[外链图片转存中…(img-qteaKy1K-1713159393630)]

[外链图片转存中…(img-vXIsVYc7-1713159393630)]

[外链图片转存中…(img-Asm6fVzU-1713159393630)]

版权声明


相关文章:

  • java 前后端交互基础技术2024-10-14 11:26:02
  • java基础的程序员2024-10-14 11:26:02
  • 大数据开发java基础122024-10-14 11:26:02
  • java基础455讲2024-10-14 11:26:02
  • 杨中科java基础2024-10-14 11:26:02
  • b站java基础入门2024-10-14 11:26:02
  • java制作网页基础2024-10-14 11:26:02
  • java 高级基础2024-10-14 11:26:02
  • c语言基础学java书2024-10-14 11:26:02
  • 宋红康java基础视频2024-10-14 11:26:02