可重入锁的理解及公平锁和非公平锁的具体实现

article/2025/9/27 6:10:54

可重入锁

首先结合以下两个例子理解以下可重入锁的概念。

/*** 可重入锁:* 1、可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。* 2、是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个* 对象),不会因为之前已经获取过还没释放而阻塞*/public class ReEnterLockDemo {static Object objectLockA = new Object();public static void m1(){new Thread(() -> {synchronized (objectLockA){System.out.println(Thread.currentThread().getName()+"\t"+"------外层调用");synchronized (objectLockA){System.out.println(Thread.currentThread().getName()+"\t"+"------中层调用");synchronized (objectLockA){System.out.println(Thread.currentThread().getName()+"\t"+"------内层调用");}}}},"t1").start();}public static void main(String[] args) {m1();}
}
public class ReEnterLockDemo {public synchronized void m1(){System.out.println("=====外层");m2();}public synchronized void m2() {System.out.println("=====中层");m3();}public synchronized void m3(){System.out.println("=====内层");}public static void main(String[] args) {new ReEnterLockDemo().m1();}
}

相关知识的了解

3种让线程等待和唤醒的方法

  • 方式1: 使用Object中的wait()方法让线程等待, 使用Object中的notify()方法唤醒线程
  • 方式2: 使用JUC包中Conditionawait()方法让线程等待,使用signal()方法唤醒线程
  • 方式3: LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

Object类提供的等待唤醒机制的缺点

public class LockSupportDemo1 {static Object objectLock = new Object();public static void main(String[] args) {new Thread(() -> {synchronized (objectLock){System.out.println(Thread.currentThread().getName()+"\t"+"------come in");try {objectLock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"\t"+"------被唤醒");}},"A").start();new Thread(() -> {synchronized (objectLock){objectLock.notify();System.out.println(Thread.currentThread().getName()+"\t"+"------通知");}},"B").start();}}

结果:

A	------come in
B	------通知
A	------被唤醒Process finished with exit code 0

异常情况①:去掉同步代码块

public class LockSupportDemo1 {static Object objectLock = new Object();public static void main(String[] args) {new Thread(() -> {
//            synchronized (objectLock){System.out.println(Thread.currentThread().getName()+"\t"+"------come in");try {objectLock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"\t"+"------被唤醒");
//            }},"A").start();new Thread(() -> {
//            synchronized (objectLock){objectLock.notify();System.out.println(Thread.currentThread().getName()+"\t"+"------通知");
//            }},"B").start();}
}    

结果:

A	------come in
Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateExceptionat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:502)at com.youth.guiguthirdquarter.AQS.LockSupportDemo1.lambda$main$0(LockSupportDemo1.java:16)at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalMonitorStateExceptionat java.lang.Object.notify(Native Method)at com.youth.guiguthirdquarter.AQS.LockSupportDemo1.lambda$main$1(LockSupportDemo1.java:26)at java.lang.Thread.run(Thread.java:748)Process finished with exit code 0

异常情况②:先唤醒,再等待。

public class LockSupportDemo1 {static Object objectLock = new Object();public static void main(String[] args) {new Thread(() -> {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}synchronized (objectLock){System.out.println(Thread.currentThread().getName()+"\t"+"------come in");try {objectLock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"\t"+"------被唤醒");}},"A").start();new Thread(() -> {synchronized (objectLock){objectLock.notify();System.out.println(Thread.currentThread().getName()+"\t"+"------通知");}},"B").start();}
}    

结果:

B	------通知
A	------come inProcess finished with exit code -1

死循环,A无法被唤醒了。

两种异常:

Object类提供的waitnotify

1、只能在synchronized同步代码块里使用

2、只能先等待(wait),再唤醒(notify)。顺序一旦出错,那个等待线程就无法被唤醒了。

Condition类提供的等待唤醒机制的缺点

这里也有两个缺点,而且和Object类里的wait,notify几乎一样。

1、只能在lock同步代码块里使用,不然就报错

2、只能先等待(await),再唤醒(signal)。顺序一旦错了,那个等待线程就无法被唤醒了。

但相对于wait,notify改进的一点是,可以绑定lock进行定向唤醒,或者说精确唤醒

LockSupport(本节重点)

首先直接看示例

异常情况①:无同步代码块

public class LockSupportDemo3 {public static void main(String[] args) {/**LockSupport:俗称 锁中断LockSupport它的解决的痛点1。LockSupport不用持有锁块,不用加锁,程序性能好,2。不需要等待和唤醒的先后顺序,不容易导致卡死*/Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t ----begin-时间:" + System.currentTimeMillis());LockSupport.park();//阻塞当前线程System.out.println(Thread.currentThread().getName() + "\t ----被唤醒-时间:" + System.currentTimeMillis());}, "t1");t1.start();LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + "\t 通知t1...");}
}

结果:

t1	 ----begin-时间:1603376148147
t1	 ----被唤醒-时间:1603376148147
main	 通知t1...Process finished with exit code 0

没有出现任何问题。

异常情况②:先唤醒,再阻塞(等待)。

public static void main(String[] args) {Thread t1 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "\t ----begin-时间:" + System.currentTimeMillis());LockSupport.park();//阻塞当前线程System.out.println(Thread.currentThread().getName() + "\t ----被唤醒-时间:" + System.currentTimeMillis());}, "t1");t1.start();LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + "\t 通知t1...");}

结果:

main	 通知t1...
t1	 ----begin-时间:1603376257183
t1	 ----被唤醒-时间:1603376257183Process finished with exit code 0

可以看到,如果提前对线程进行唤醒。那么后面执行的LockSupport.park();就相当于瞬间被唤醒了,不会和之前一样程序卡死。

为什么呢?

static void park()  //除非许可证可用,否则禁用当前线程以进行线程调度。 
static void	unpark(Thread thread)	//如果给定线程尚不可用,则为其提供许可。
  • LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
  • LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),
    permit只有两个值1和零,默认是零。可以把许可看成是一种(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。
public static void park() {UNSAFE.park(false, 0L);
}

LockSupport底层是通过UNSAFE。

  • permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为0并返回。

  • 调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(多次调用unpark方法,不会累加,permit值还是1),然后程序就会自动唤醒thread线程,也就是之前阻塞中的LockSupport.park()方法会立即返回。

  • LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpark就将0变成1,调用一次park会消费permit,也就是将1变成o,同时park立即返回。如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1。
    每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累凭证。

  • 简单的理解就是,线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。

    当调用park方法时:
    如果有凭证,则会直接消耗掉这个凭证然后正常退出;
    如果无凭证,就必须阻塞等待凭证可用。
    而unpark则相反:
    它会增加一个凭证,但凭证最多只能有1个,累加无效。
    

    因此上述问题的答案就是:

1、先执行unpark(),将许可证由0变为1。

2、然后park()来了发现许可证此时为0(也就是有许可证),那么他就不会阻塞,马上就往后执行。同时消耗许可证(也就是将1又变为0)。

回归正题

Java中的synchronizedReentrantLock 都是可重入锁。可重入锁的意义在于防止死锁

先看下ReentrantLock的继承关系图:
在这里插入图片描述

ReentrantLock实现了Lock接口,对外提供Lock接口的方法。有一个同步器属性,上锁、释放锁都是通过调用同步器的相关方法实现的。构造时,同步器可以选择公平锁/非公平锁,它们都继承了抽象父类Sync,而Sync又继承了AQS
抽象类AQS维护了等待队列,而ReentrantLock只需要定义共享资源的获取与释放的方式。

可重入功能的实现原理

ReentrantLock的可重入功能基于AQS的同步状态:state。
其原理大致为:当某一线程获取锁后,将state值+1,并记录下当前持有锁的线程,再有线程来获取锁时,判断这个线程与持有锁的线程是否是同一个线程,如果是,将state值再+1,如果不是,阻塞线程。 当线程释放锁时,将state值-1,当state值减为0时,表示当前线程彻底释放了锁,然后将记录当前持有锁的线程的那个字段设置为null,并唤醒其他线程,使其重新竞争锁。
以下是非公平锁(默认下我们使用的都是非公平锁)中可重入的实现原理。

public ReentrantLock() {// 默认非公平锁sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

这是非公平锁的部分代码,这里只为说明可重入的实现原理(详见注释),对于非公平锁更详细分析见后文。

// acquires的值是1
final boolean nonfairTryAcquire(int acquires) {// 获取当前线程final Thread current = Thread.currentThread();// 获取state的值int c = getState();// 如果state的值等于0,表示当前没有线程持有锁// 尝试将state的值改为1,如果修改成功,则成功获取锁,并设置当前线程为持有锁的线程,返回trueif (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}// state的值不等于0,表示已经有其他线程持有锁// 判断当前线程是否等于持有锁的线程,如果等于,将state的值+1,并设置到state上,获取锁成功,返回true// 如果不是当前线程,获取锁失败,返回falseelse if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
}

ReentrantLock中非公平和公平的实现原理

因为ReentrantLock同时支持公平锁和非公平锁,上文也提到, ReentrantLock默认无参构造函数使用的是非公平锁,有参构造函数可指定使用公平锁。
对于公平锁:是指在获取锁之前会检查队列中有没有线程在等待,如果有的话就不会去获取锁,而是会从尾结点加入队列。
对于非公平锁:就是在获取锁之前不会去检查队列中有没有线程在等待,而是直接去获取锁,这里其实是一种插队的表现。如果锁没有线程占用,则队列中被唤醒的线程和新来的线程会同时竞争锁。此时,队列中被唤醒的线程并不一定能优先获得锁,当队列中被唤醒的线程被新来的线程抢占了资源,这种插队也就表现出了非公平的特性

非公平锁的获取

注意和后面公平锁的对比

static final class NonfairSync extends Sync {final void lock() {// 和公平锁相比,这里会直接先进行一次CAS(尝试插队),成功就返回了。这是第一处不一样【对比请看下方公平锁的lock】if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}// AbstractQueuedSynchronizer类的acquire(int arg)方法public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
}// acquires的值是1
final boolean nonfairTryAcquire(int acquires) {// 获取当前线程final Thread current = Thread.currentThread();// 获取state的值int c = getState();// 如果state的值等于0,表示当前没有线程持有锁// 尝试将state的值改为1,如果修改成功,则成功获取锁,并设置当前线程为持有锁的线程,返回trueif (c == 0) {// 这里没有对队列进行判断,直接CAS抢,这是第二点不一样【对比请看下方公平锁的lock】if (compareAndSetState(0, acquires)) {//获取成功就设置线程变量setExclusiveOwnerThread(current);return true;}}// state的值不等于0,表示已经有其他线程持有锁// 判断当前线程是否等于持有锁的线程,如果等于,将state的值+1,并设置到state上,获取锁成功,返回true// 如果不是当前线程,获取锁失败,返回falseelse if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;// 如果小于0,那么表示重入次数过多(超过了Integer.MAX_VALUE),直接抛出异常// 新的state小于0的情况,只有c为Integer.MAX_VALUE时才会发生,// 由于计算机二进制的计算原理,此时加上1反而会变成int类型的最小值,从而小于0if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");//这里不需要CAS,因为在else if条件中,当前线程就是已经获取到锁的线程了setState(nextc);return true;}return false;
}

公平锁的获取

static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;// 获取锁,与非公平锁的不同的地方在于,这里直接调用的AQS的acquire方法,没有先尝试获取锁// acquire又调用了下面的tryAcquire方法,核心在于这个方法final void lock() {acquire(1);}// 这个方法和nonfairTryAcquire方法只有一点不同,就是 !hasQueuedPredecessors()// 多了一个判断hasQueuedPredecessors,这个方法是判断当前AQS的同步队列中是否还有等待的线程// 如果有,返回false,否则返回true。// 由此可知,当队列中没有等待的线程时,当前线程才能尝试通过CAS的方式获取锁。// 否则就让这个线程去队列后面排队。protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// 和非公平锁相比,这里多了一个判断:是否有线程在队列列等待,有我就不抢了if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}// state的值不等于0,表示已经有其他线程持有锁// 判断当前线程是否等于持有锁的线程,如果等于,将state的值+1,并设置到state上,获取锁成功,返回true// 如果不是当前线程,获取锁失败,返回falseelse if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
}

公平锁中AQS提供的同步队列机制:把线程封装成节点,在一个队列中进行排队。(队列是一个双向链表)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OfCDrCOQ-1650633457805)(C:\Users\86135\AppData\Roaming\Typora\typora-user-images\1650603144662.png)]

线程在排队过程中,可能存在多个线程共同竞争尾节点的位置,这个过程其实也是通过CAS+自旋来实现的,因此其实公平锁并非绝对公平
在这里插入图片描述

注意:第一个节点是空节点(哨兵),指的不是Node为空,而是Thread = null 。获得锁的线程不在队列中,第二个位置才是即将获得资源的线程。释放锁的时候,是通过类似head.unpark(head.next.Thread)这个逻辑的伪代码来实现的,也就是线程1来unpark(线程2)

在这里插入图片描述

注意线程节点入队后的操作流程理解:

入队后,需要把前面一个节点(线程)的waitstates改为-1,然后park(this),进入阻塞等待;waitstates = -1的意义:如果该节点后面有节点(线程阻塞等待),需要cur.unpark(next)后面的线程,停止阻塞。waitstates = 0的意义: 队列后面没有节点了,不需要unpark()其他线程。当线程从队列中获取到了锁,执行后,该线程的节点 thread = null ,就是队列中的第一个节点。

锁的释放

解锁过程,unlock()方法是在AQS中实现的:

public void unlock() {// 直接调用了AQS中的release方法,参数为1表示解锁一次state值-1sync.release(1);   
}
public final boolean release(int arg) {// 和tryAcquire一样,也得子类去重写,释放锁操作if (tryRelease(arg)) {   // 释放锁成功后,获取新的头结点Node h = head;// 如果新的头结点不为空并且不是刚刚建立的结点(初始状态下status为默认值0,而上面在进行了shouldParkAfterFailedAcquire之后,会被设定为SIGNAL状态,值为-1)    if (h != null && h.waitStatus != 0)   // 唤醒头节点下一个节点中的线程unparkSuccessor(h);   return true;}return false;
}
private void unparkSuccessor(Node node) {// 将等待状态waitStatus设置为初始值0int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);// 获取下一个结点Node s = node.next;// 如果下一个结点为空或是等待状态是已取消,那肯定是不能通知unpark的,这时就要遍历所有节点再另外找一个符合unpark要求的节点if (s == null || s.waitStatus > 0) {   s = null;//这里是从队尾向前,因为enq()方法中的t.next = node是在CAS之后进行的,而 node.prev = t 是CAS之前进行的,所以从后往前一定能够保证遍历所有节点for (Node t = tail; t != null && t != node; t = t.prev)   if (t.waitStatus <= 0)s = t;}//如果找到了,就直接unpark,如果还是没找到,只能就此作罢if (s != null)   LockSupport.unpark(s.thread);
}

再来看看tryRelease()方法是怎么实现的,具体实现在Sync中:

protected final boolean tryRelease(int releases) {// 先计算本次解锁之后的状态值int c = getState() - releases;  // 因为是独占锁,那肯定这把锁得是当前线程持有才行if (Thread.currentThread() != getExclusiveOwnerThread()) // 否则直接抛异常  throw new IllegalMonitorStateException();   boolean free = false;// 如果解锁之后的值为0,表示已经完全释放此锁if (c == 0) {  free = true;// 将独占锁持有线程设置为nullsetExclusiveOwnerThread(null);  }// 状态值设定为csetState(c); // 如果不是0表示此锁还没完全释放,返回false,是0就返回true  return free;  
}

总结

  1. Java中的synchronizedReentrantLock都是可重入锁(可递归锁)。可重入锁的意义在于防止死锁

  2. 由于synchronized是基于monitor机制实现的,它只支持非公平锁

  3. ReentrantLock同时支持公平锁和非公平锁,它依赖LockSupportpark()unpark()方法实现。


http://chatgpt.dhexx.cn/article/aSD07rvY.shtml

相关文章

公平锁与非公平锁的核心原理

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章是关于并发编程中AQS的独占模式和共享模式。 本篇文章记录的基础知识&#xff0c;适合在学Java的小白&#xff0c;也适合复习中&#xff0c;面试中的大佬&#x1…

公平锁和非公平锁介绍,为什么要“非公平”?

什么是公平和非公平 公平锁指的是按照线程请求的顺序&#xff0c;来分配锁&#xff1b;而非公平锁指的是不完全按照请求的顺序&#xff0c;在一定情况下&#xff0c;可以允许插队。但需要注意这里的非公平并不是指完全的随机&#xff0c;不是说线程可以任意插队&#xff0c;而…

图解ReentrantLock公平锁和非公平锁实现

概述 ReentrantLock是Java并发中十分常用的一个类&#xff0c;具备类似synchronized锁的作用。但是相比synchronized, 它具备更强的能力&#xff0c;同时支持公平锁和非公平锁。 公平锁&#xff1a; 指多个线程按照申请锁的顺序来获取锁&#xff0c;线程直接进入队列中排队&a…

Java面试突击:公平锁和非公平锁有什么区别?

从公平的角度来说&#xff0c;Java 中的锁总共可分为两类&#xff1a;公平锁和非公平锁。但公平锁和非公平锁有哪些区别?孰优孰劣呢?在 Java 中的应用场景又有哪些呢?接下来我们一起来看。 正文公平锁&#xff1a;每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的&am…

浅谈ReentrantLock的公平锁和非公平锁的区别

前言 最近在看java并发编程这本书&#xff0c;已经看了点ReentrantLock的源码&#xff0c;以及之前有面试官问&#xff0c;公平锁和非公平锁有啥区别&#xff0c;我就只是从源码层面说了一下区别&#xff0c;但在性能上也有区别&#xff0c;今天就来说道说道。 公平与非公平 …

aqs原理初探以及公平锁和非公平锁实现

深入理解AQS 一&#xff0c;AQS1&#xff0c;ReentrantLock2&#xff0c;CAS3&#xff0c;AbstractQueuedSynchronizer3.1&#xff0c;FairSync3.2&#xff0c;NofairSync3.3&#xff0c;AQS中几个重要的相关参数3.4&#xff0c;Node 一&#xff0c;AQS AbstractQueuedSynchro…

图解ReentrantLock底层公平锁和非公平锁实现原理

&#x1f4bb;在面试或者日常开发当中&#xff0c;经常会遇到公平锁和非公平锁的概念。 两者最大的区别如下&#x1f447; 1️⃣ 公平锁&#xff1a;N个线程去申请锁时&#xff0c;会按照先后顺序进入一个队列当中去排队&#xff0c;依次按照先后顺序获取锁。就像下图描述的上…

ReentrantLock之公平锁和非公平锁详解

ReentrantLock是一个互斥锁&#xff0c;它具有synchronized相同的能力&#xff1b;但相比之下&#xff0c;ReentrantLock扩展性更强&#xff0c;比如实现了公平锁。 下面详细拆解下ReentrantLock的公平锁和非公平锁的实现。 JDK版本&#xff1a;1.8.0_40 公平锁 先看Reentr…

ReentrantLock中公平锁和非公平锁的区别

目录 背景知识 ReentrantLock的组成 概述 公平锁示意图 非公平锁示意图 源码解读 非公平锁 公平锁 代码对比 问题 知识扩展 tryLock方法 参考资料 背景知识 ReentrantLock的组成 首先看下ReentrantLock的组成结构。 公平锁和非公平锁主要是通过内部类FairSync和…

公平锁和非公平锁

Reentrant Re entrant&#xff0c;Re是重复、又、再的意思&#xff0c;entrant是enter的名词或者形容词形式&#xff0c;翻译为进入者或者可进入的&#xff0c;所以Reentrant翻译为可重复进入的、可再次进入的&#xff0c;因此ReentrantLock翻译为重入锁或者再入锁。 公平锁…

阿里面试官:说一下公平锁和非公平锁的区别?

点赞再看&#xff0c;养成习惯&#xff0c;微信搜索【三太子敖丙】关注这个互联网苟且偷生的工具人。 本文 GitHub https://github.com/JavaFamily 已收录&#xff0c;有一线大厂面试完整考点、资料以及我的系列文章。 前言 上次我们提到了乐观锁和悲观锁&#xff0c;那我们知道…

Ubuntu 手动安装 JDK8

文章目录 1. 下载2. 解压安装3. 配置环境变量 1. 下载 先去官网下载合适的版本&#xff0c;官网&#xff1a;https://www.oracle.com/java/technologies/downloads/archive/ 通过下载页面获取到下载链接后&#xff0c;可以直接在Ubuntu上使用wget下载&#xff0c;也可以先下载…

centos8安装jdk教程

文章目录 一、安装二、配置环境变量三.验证 一、安装 1、查看JDK软件包列表 yum search java | grep -i --color jdk2、选择版本安装 yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel或者如下命令安装jdk8所有文件 yum install -y java-1.8.0-openjdk*二、配置…

Java - JDK8安装及配置环境变量教程

Java - JDK8安装及配置环境变量教程 一、安装JDK教程 甲骨文官网下载JDK版本&#xff1a;windows64下载地址 下载完成后开始安装JDK&#xff1a;双击打开 点击下一步&#xff1a; 若不需要自定义路径&#xff0c;则安装到默认路径即可&#xff08;安装的路径需记住&#xff0…

JDK8安装和环境配置

JDK8的安装和环境配置 一、JDK8下载二、安装三、环境配置 一、JDK8下载 官网下载&#xff1a; https://www.oracle.com/java/technologies/downloads/#java8-windows 二、安装 打开安装&#xff0c;一直下一步即可&#xff0c;可以在安装过程中更改安装地址&#xff0c;我放…

Java JDK 8的安装与配置

文章目录 前言1. 安装JDK 8Step1&#xff1a;选择JDK的版本Step2&#xff1a;选择系统平台Step3&#xff1a;下载安装包Step4&#xff1a;开始安装 2. 配置JDK 8Step1&#xff1a;配置“环境变量path” 前言 本教程是在Windows 64位平台上安装JDK 8版本。 1. 安装JDK 8 官网…

JDK8安装与环境配置

前言&#xff1a;在网上看了下JDK的安装与环境配置&#xff0c;发现很多视频以及博客的讲的都很复杂&#xff0c;对初学者很不友好。很多小白看到这些配置步骤都一脸懵&#xff0c;即使一步一步看着操作还是配置失败。这主要是因为很多博主配置了不需要的配置环境&#xff0c;让…

Linux安装JDK8详细图文教程

第一步、获取JDK文件 JDK下载包&#xff1a;直接进入 如果跳转登录页面&#xff0c;注册一个账号登录即可 登录过后文件就下载完成 第二步、上传JDK到服务器 1、创建JDK目录 mkdir -p /developer/env/jdk进入目录 cd /developer/env/jdk2、安装lrzsz&#xff08;用于远程传…

JDK8安装教程(Windows、Ubuntu)

文章目录 下载JDK8在Windows上安装设置环境变量 在Ubuntu上安装 下载JDK8 Linux版&#xff0c;CSDN下载地址 推荐官网下载&#xff08;需要注册&#xff09;&#xff0c;能下到Windows和Ubuntu的最新版 在Windows上安装 双击打开下载好的安装包&#xff0c;点击下一步 点击左…

linux系统安装jdk8详细教程

文章目录 前言一、下载jdk8的安装包二、压缩包上传解压1.将下载好的压缩包使用ftp工具上传到服务器2.将压缩包解压到指定目录 三、配置jdk的环境变量四、测试是否安装成功 前言 虚拟机版本&#xff1a;centos7 jdk版本&#xff1a;1.8 一、下载jdk8的安装包 方法1&#xff1a…