萌新的Java多线程基础(下,后来把上的部分直接合并了,感觉这样方便理解)

    技术2025-07-25  21

    线程与进程

    进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

    线程:进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程。线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。

        为什么程序计数器必须私有?

        A: 程序计数器记录了该线程的当前执行状态。

        为什么栈必须私有?

        A: 虚拟机栈和本地方法栈是线程用来保存自己操作现场的,为了保证线程自己的局部变量不被其他线程访问,所以必须私有。

    JMM内存模型

    稍微解释一下这个图:每个线程操作变量的流程大致为:通过JMM从主内存将变量拷贝副本到线程内,然后在线程自己的内存中进行操作,然后再通过JMM同步回主内存。(Tips:这里其实就蕴含了volatile的原理)

    线程调度

    分时调度:

    所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

    抢占式调度:

    优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

    CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

     

    同步与异步

    同步:   排队执行 , 效率低但是安全.

    异步:   同时执行 , 效率高但是数据不安全.

     

    并发与并行

    并发:  指两个或多个事件在同一个时间段内发生。

    并行:  指两个或多个事件在同一时刻发生(同时发生)。

     

    每个线程执行过程中所调用的方法都在该线程的内存空间(线程自己的栈空间)里运行。

    每个线程都拥有一份自己独有的栈空间,线程与线程之间共用一份堆内存。

     

    并发编程三大特性: 原子性,可见性,顺序性。

    关于原子性:

        Java中除了long和double,其他基础类型的赋值都是原子性的。

        Java中++和--操作不是原子性的。

    简要记述:

    volatile关键字 可以保证可见性和顺序性AtomicInteger 可以保证 ++ 和 -- 的原子性复合操作的原子性可以用 synchronized 关键字

    Thread

    Thread类里面的stop()方法已过时,可以通过声名一个标志变量,然后判断此变量状态使其返回run()的方式,让他自杀。(PS:返回值是void,所以直接return;即可)实现Runnable与继承Thread相比有如下优势: 1、通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况。 2、主要是可以避免单继承带来的局限性。3、任务与线程本身是分离的,提高了程序的健壮性。 4、后续学习的线程池技术,接受Runnable类型的任务,不接收Thread类型的线程。线程:分为守护线程和用户线程 用户线程:当一个进程不包含任何存活的用户线程时,进程结束。 守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。

    想要防止死锁的话,在一个可能会调用锁的区域,就不要再写另一个可能会调用锁的操作。

    1、Java虚拟机中线程有六种状态,具体怎么回事就见名知意了

    1, new    2, runnable    3,  blocked   4, waiting   5, time_waiting   6, terminal

    2、操作系统中的线程状态应该分为5种,如下图:

    值得强调的是:Runnable状态是一个就绪的状态,并不是正在竞争锁,而是目前没有调度到该线程

    进入Terminated状态有三种可能:1、正常结束2、程序异常3、JVM异常关闭。

    Volatile和sychronized的比较:

    a. volatile的本质是在告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主内存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

    b. volatile只能用在变量级别;synchronized可以使用在变量、方法和类级别。

    c. volatile只能实现变量的修改可见性,不能保证原子性;synchronized则可以保证变量的修改可见性和原子性。

    d. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

    e. volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

    什么是CAS:

    Atomic操作的底层实现利用的是CAS机制,CAS = Compare And Swap,比较并交换

    CAS 机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B

    Synchronized与CAS;悲观锁与乐观锁、自旋锁:

    Synchronized是悲观锁,这种线程一旦得到锁,其他需要锁的线程就挂起的情况就是悲观锁。

    CAS操作的就是乐观锁,每次不加锁而是假设没有冲突去完成某项操作,如果因为冲突而失败就重试,知道成功为止。因为失败时线程会不断循环重试,所以又称为自旋锁。

    Synchronized缺点:

        Synchronized 关键字会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,这个过程中涉及到的操作系统用户模式和内核模式的转换,代价比较高。

    CAS 的缺点:

    CPU 开销较大:并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给 CPU 带来很大的压力。不能保证代码块的原子性:CAS 机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用 Synchronized 了。

    Wait 与 Notify :

    a. Wait 与 notify 提供对同一个锁对象的线程之间更精细的同步操作。

    b. 使用wait(), notify() 时首先需要对调用对象加锁。

    c. 调用 wait() 方法后,线程状态会从RUNNING 变为 WAITING ,并将当前线程加入到 lock 对象的等待队列中。

    d. 其他某个锁住同一对象的线程,在调用 notify() 后,会将等待队列中的一个线程移到 lock 对象的同步队列,被移动的线程状态由BLOCKED 变为 RUNNABLE

    e. 被移动到同步队列中的线程,必须要等到 notify() 的调用者将锁释放,才有机会从同步队列返回。这里只是有机会,因为锁释放后,等待线程会出现竞争,只有竞争到该锁的线程才会从 wait() 方法返回,其他的线程只能继续等待。

    (注:针对初学者,对 d、e两条的附加解释: 

          假如一个线程a0调用了notifyAll(),然后会发生的事情是       blocked中所有锁对象相同的线程全部进入同步队列,他们的状态也变成了runnable      然后等到a0释放了锁资源后,同步队列中要竞争这个锁对象的线程开始竞争       最后在同步队列中没竞争到锁的,就从runnable挂起到blocked了。竞争到的就进入running )

    wait()与sleep()的区别:

            sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。         wait是0bject类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

    Processed: 0.017, SQL: 9