Jdk1.7 JUC源码增量解析(6)-Phaser
作者:大飞
- Phaser是jdk1.7提供的类似于CyclicBarrier和CountDownLatch的同步机制。
- 它支持更灵活的使用方式:1.使用过程中可以随时注册和注销参与者;2.不同于CyclicBarrier,分离出"到达"和"等待"机制;3.支持结束,默认情况下,当没有参与者的时候Phaser就结束了;4.支持层级Phaser结构;5.提供针对内部状态的监控方法;
- 先看一下内部结构:
/** * 主状态,分为4部分: * * 未到达计数 -- 还没有到达栅栏的参与者计数。 (bits 0-15) * parties -- 栅栏全部参与者的计数。 (bits 16-31) * phase -- 栅栏当前所处的阶段 (bits 32-62) * terminated -- 栅栏的结束标记 (bit 63 / sign) * * 一个没有注册参与者的phaser的主状态中会有0个参与者计数 * 和1个未到达计数。 */ private volatile long state; private static final int MAX_PARTIES = 0xffff; private static final int MAX_PHASE = Integer.MAX_VALUE; private static final int PARTIES_SHIFT = 16; private static final int PHASE_SHIFT = 32; private static final int UNARRIVED_MASK = 0xffff; // to mask ints private static final long PARTIES_MASK = 0xffff0000L; // to mask longs private static final long TERMINATION_BIT = 1L << 63; // some special values private static final int ONE_ARRIVAL = 1; private static final int ONE_PARTY = 1 << PARTIES_SHIFT; private static final int EMPTY = 1;
主状态这样设计(将状态封装到一个原子的long域)可以从两方面提高性能,一个是对状态的编解码简单高效、另一个是可以减小竞争窗口(空间)。
/** * 当前phaser的父phaser, 如果没有父phaser,这个域为null。 */ private final Phaser parent; /** * phaser树的根节点. 如果当前phaser不在一棵树内,这个域等于自身。 */ private final Phaser root;
private final AtomicReference<QNode> evenQ; private final AtomicReference<QNode> oddQ; private AtomicReference<QNode> queueFor(int phase) { return ((phase & 1) == 0) ? evenQ : oddQ; }
再看下QNode这个类,先看下结构:
static final class QNode implements ForkJoinPool.ManagedBlocker { final Phaser phaser; final int phase; final boolean interruptible; final boolean timed; boolean wasInterrupted; long nanos; long lastTime; volatile Thread thread; // nulled to cancel wait QNode next; QNode(Phaser phaser, int phase, boolean interruptible, boolean timed, long nanos) { this.phaser = phaser; this.phase = phase; this.interruptible = interruptible; this.nanos = nanos; this.timed = timed; this.lastTime = timed ? System.nanoTime() : 0L; thread = Thread.currentThread(); }
继续看下QNode中的方法,首先是isReleasable方法:
public boolean isReleasable() { if (thread == null) return true; if (phaser.getPhase() != phase) { thread = null; return true; } if (Thread.interrupted()) wasInterrupted = true; if (wasInterrupted && interruptible) { thread = null; return true; } if (timed) { if (nanos > 0L) { long now = System.nanoTime(); nanos -= now - lastTime; lastTime = now; } if (nanos <= 0L) { thread = null; return true; } } return false; }
再看下block方法:
public boolean block() { if (isReleasable()) return true; else if (!timed) LockSupport.park(this); else if (nanos > 0) LockSupport.parkNanos(this, nanos); return isReleasable(); }
- 通过一些示例来分析下主要功能源码。
首先看一个示例,使用方式类似于Count为1的CountDownLatch:
public static void main(String[] args) { final Phaser phaser = new Phaser(1); for(int i=0;i<10;i++){ phaser.register(); new Thread(new Runnable() { @Override public void run() { phaser.arriveAndAwaitAdvance(); System.out.println(Thread.currentThread() +" start!!!!"); } }).start(); } try { TimeUnit.SECONDS.sleep(5); phaser.arriveAndDeregister(); } catch (InterruptedException e) { e.printStackTrace(); } }
示例中首先构造了一个Phaser,我们先看下构造方法:
/** * 创建一个没有初始参与者的phaser,默认没有父级phaser,初始 * phase值为0。如果有任何线程想要使用这个phaser,都必须先 * 注册这个phaser。 */ public Phaser() { this(null, 0); } /** * 创建一个有初始参与者(未到达)数量的phaser,默认没有父级phaser,初始 * phase值为0。 */ public Phaser(int parties) { this(null, parties); } public Phaser(Phaser parent) { this(parent, 0); } /** * 创建一个有给定父级phaser和初始参与者(未到达)数量的phaser, * 如果给定的父级phaser不为null,并且给定的参与者数量大于0, * 当前的子phaser相当于注册了父phaser。 */ public Phaser(Phaser parent, int parties) { //parties不能超过65535 if (parties >>> PARTIES_SHIFT != 0) throw new IllegalArgumentException("Illegal number of parties"); int phase = 0; this.parent = parent; if (parent != null) { //如果父级Phaser不为空。 final Phaser root = parent.root; this.root = root; //共享父级的线程等待队列。 this.evenQ = root.evenQ; this.oddQ = root.oddQ; if (parties != 0) //如果当前phaser的参与者不为0,那么注册一个参与者到父级,注意这里是一个。 phase = parent.doRegister(1); } else { //如果父级为空。 //root就是自身, this.root = this; this.evenQ = new AtomicReference<QNode>(); this.oddQ = new AtomicReference<QNode>(); } this.state = (parties == 0) ? (long)EMPTY : ((long)phase << PHASE_SHIFT) | ((long)parties << PARTIES_SHIFT) | ((long)parties); }
示例中接下来会在每次新建线程之前调用register方法来注册参与者,看下这个方法:
/** * 添加一个新的未到达的参与者到当前phaser。如果当前正在onAdvance方法, * 的执行过程中,这个方法会等待其完成再返回。如果当前phaser有父phaser, * 并且当前phaser之前没有注册的参与者,phaser会注册到父phaser上。 * 如果当前phaser结束了,那么方法不会产生任何作用,并返回一个负数。 */ public int register() { return doRegister(1); }
register内部调用的是doRegister,看下这个方法:
private int doRegister(int registrations) { //调整主状态,将给定的数值加到总参与者和未到达参数者数量上。 long adj = ((long)registrations << PARTIES_SHIFT) | registrations; final Phaser parent = this.parent; int phase; for (;;) { long s = state; int counts = (int)s; int parties = counts >>> PARTIES_SHIFT; int unarrived = counts & UNARRIVED_MASK; if (registrations > MAX_PARTIES - parties) //注册的参与者数量和已存在的参与者数量加起来不能超过最大参与者数量。 throw new IllegalStateException(badRegister(s)); else if ((phase = (int)(s >>> PHASE_SHIFT)) < 0) //如果phaser已经结束,那么直接退出循环。 break; else if (counts != EMPTY) { //如果不是第一个注册。 if (parent == null || reconcileState() == s) { if (unarrived == 0) // 如果当前未到达数量为0,说明需要进入下一阶段了,这里要等待一下root进入下一阶段。 root.internalAwaitAdvance(phase, null); //否则原子更新主状态。 else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s + adj)) break; } } else if (parent == null) { // 第一个root注册(没有父级)。 // 算出下一个主状态。 long next = ((long)phase << PHASE_SHIFT) | adj; // 原子更新主状态。 if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next)) break; } else { //第一个子phaser的注册,需要加锁。 synchronized (this) { if (state == s) { //检测一下状态有没有变化。 parent.doRegister(1); //由于是第一次注册,所以需要向父类注册一下。 do { //更新到下一个主状态。 phase = (int)(root.state >>> PHASE_SHIFT); // assert phase < 0 || (int)state == EMPTY; } while (!UNSAFE.compareAndSwapLong (this, stateOffset, state, ((long)phase << PHASE_SHIFT) | adj)); break; } } } } return phase; }
doRegister方法中会调用reconcileState来调整状态,看下这个方法实现:
private long reconcileState() { final Phaser root = this.root; long s = state; if (root != this) { int phase, u, p; // CAS root phase with current parties; possibly trip unarrived while ((phase = (int)(root.state >>> PHASE_SHIFT)) != (int)(s >>> PHASE_SHIFT) && !UNSAFE.compareAndSwapLong (this, stateOffset, s, s = (((long)phase << PHASE_SHIFT) | (s & PARTIES_MASK) | ((p = (int)s >>> PARTIES_SHIFT) == 0 ? EMPTY : (u = (int)s & UNARRIVED_MASK) == 0 ? p : u)))) s = state; } return s; }
doRegister方法中,如果当前Phaser正在进入下一阶段过程中,需要等待这个过程完成,会调用internalAwaitAdvance方法,看下这个方法:
/** cpu核数 */ private static final int NCPU = Runtime.getRuntime().availableProcessors(); /** * 单个参与者阻塞等待栅栏进入下一个阶段之前的自旋次数。 * 在多核处理器下,一次性完全的阻塞和唤醒一大批线程通常比较慢, * 所以我们这里使用了一个可调整的自旋次数值在避免这种情况。 * 当一个参与者线程在internalAwaitAdvance方法中阻塞之前发现了 * 其他到达的线程,并且有cpu资源可用,那么这个参与者线程会在阻塞 * 之前自旋SPINS_PER_ARRIVAL或者更多次。 */ static final int SPINS_PER_ARRIVAL = (NCPU < 2) ? 1 : 1 << 8; private int internalAwaitAdvance(int phase, QNode node) { releaseWaiters(phase-1); // 清空不用的等待线程队列(Treiber Stack)。 boolean queued = false; // 入队标识。 int lastUnarrived = 0; // 用于在发生变化时增加自旋次数。 int spins = SPINS_PER_ARRIVAL; long s; int p; while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) { if (node == null) { int unarrived = (int)s & UNARRIVED_MASK; //如果未到达参与者数量发生了变化,且变化后的未到达数量小于cpu核数,需要增加自旋次数。 if (unarrived != lastUnarrived && (lastUnarrived = unarrived) < NCPU) spins += SPINS_PER_ARRIVAL; //获取并清除当前线程中断标记。 boolean interrupted = Thread.interrupted(); if (interrupted || --spins < 0) { //如果当前线程被中断,或者自旋次数用完。创建一个(不可中断的)节点。 node = new QNode(this, phase, false, false, 0L); node.wasInterrupted = interrupted; } } else if (node.isReleasable()) // done or aborted break; else if (!queued) { // 将节点加入队列首部。 AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ; QNode q = node.next = head.get(); if ((q == null || q.phase == phase) && (int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq queued = head.compareAndSet(q, node); } else { try { //阻塞等待。 ForkJoinPool.managedBlock(node); } catch (InterruptedException ie) { node.wasInterrupted = true; } } } if (node != null) { if (node.thread != null) node.thread = null; // avoid need for unpark() if (node.wasInterrupted && !node.interruptible) //不可中断模式下要传递中断。 Thread.currentThread().interrupt(); if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase) return abortWait(phase); // possibly clean up on abort } releaseWaiters(phase); return p; }
注意整个internalAwaitAdvance过程的前后都会清空一下当前不用的等待线程队列(两个奇偶队列交替使用),看下这个方法:
private void releaseWaiters(int phase) { QNode q; // first element of queue Thread t; // its thread AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ; while ((q = head.get()) != null && q.phase != (int)(root.state >>> PHASE_SHIFT)) { if (head.compareAndSet(q, q.next) && (t = q.thread) != null) { q.thread = null; LockSupport.unpark(t); } } }
internalAwaitAdvance中还可能会调用一个放弃等待的abortWait方法,看下:
private int abortWait(int phase) { AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ; for (;;) { Thread t; QNode q = head.get(); int p = (int)(root.state >>> PHASE_SHIFT); if (q == null || ((t = q.thread) != null && q.phase == p)) return p; if (head.compareAndSet(q, q.next) && t != null) { q.thread = null; LockSupport.unpark(t); } } }
再回头看我们的示例,for循环中新建的线程在运行时会先调用arriveAndAwaitAdvance方法,然后会在这个方法上等待,直到主线程调用了arriveAndDeregister。看下arriveAndAwaitAdvance这个方法:
public int arriveAndAwaitAdvance() { final Phaser root = this.root; for (;;) { //获取主状态 long s = (root == this) ? state : reconcileState(); //获取phase值 int phase = (int)(s >>> PHASE_SHIFT); int counts = (int)s; //获取当前未到达参与者计数,就是之前的未到达计数减1。 int unarrived = (counts & UNARRIVED_MASK) - 1; if (phase < 0) return phase; //如果当前phaser已经结束,退出。 else if (counts == EMPTY || unarrived < 0) { if (reconcileState() == s) throw new IllegalStateException(badArrive(s)); //非法状态。 } //主状态中未到达参与者的计数减1。 else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s -= ONE_ARRIVAL)) { if (unarrived != 0) //如果还有未到达的参与者,等待。 return root.internalAwaitAdvance(phase, null); if (root != this) //如果当前是子级Phaser,要等待父级进入下一阶段。 return parent.arriveAndAwaitAdvance(); long n = s & PARTIES_MASK; // base of next state //这里算出来的是总参与者的数量。 int nextUnarrived = (int)n >>> PARTIES_SHIFT; //调用onAdvance方法。 if (onAdvance(phase, nextUnarrived)) //如果onAdvance方法返回true,给主状态中设置结束标记。 n |= TERMINATION_BIT; else if (nextUnarrived == 0) //如果总参与者数量变为0,那么将主状态设置为没有参与者的特殊状态。 n |= EMPTY; else //否则,重置未到达参与者数量。 n |= nextUnarrived; //算出下一个phase值。 int nextPhase = (phase + 1) & MAX_PHASE; //设置到主状态上。 n |= (long)nextPhase << PHASE_SHIFT; //原子更新主状态。 if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n)) //如果发生竞争,返回phase值,如果当前phaser结束,返回负数。 return (int)(state >>> PHASE_SHIFT); // terminated //清空上一阶段使用的线程等待队列。 releaseWaiters(phase); //最后返回上面算出来的nextPhase值。 return nextPhase; } } }
看一下arriveAndAwaitAdvance方法中调用的onAdvance方法:
protected boolean onAdvance(int phase, int registeredParties) { return registeredParties == 0; }
回到示例的最后,主线程休眠5秒中,然后调用了arriveAndDeregister方法,看下这个方法:
public int arriveAndDeregister() { return doArrive(true); }
这个方法表示一个参与者到达栅栏,并且将自己从phaser上注销。内部调用了doArrive方法:
private int doArrive(boolean deregister) { int adj = deregister ? ONE_ARRIVAL|ONE_PARTY : ONE_ARRIVAL; final Phaser root = this.root; for (;;) { long s = (root == this) ? state : reconcileState(); int phase = (int)(s >>> PHASE_SHIFT); int counts = (int)s; int unarrived = (counts & UNARRIVED_MASK) - 1; if (phase < 0) return phase; else if (counts == EMPTY || unarrived < 0) { if (root == this || reconcileState() == s) throw new IllegalStateException(badArrive(s)); } else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adj)) { if (unarrived == 0) { long n = s & PARTIES_MASK; // base of next state int nextUnarrived = (int)n >>> PARTIES_SHIFT; if (root != this) //这里注意下:如果当前子phaser中没有参与者了,就要于从父phaser中将当前子phaser注销。 return parent.doArrive(nextUnarrived == 0); if (onAdvance(phase, nextUnarrived)) n |= TERMINATION_BIT; else if (nextUnarrived == 0) n |= EMPTY; else n |= nextUnarrived; n |= (long)((phase + 1) & MAX_PHASE) << PHASE_SHIFT; UNSAFE.compareAndSwapLong(this, stateOffset, s, n); releaseWaiters(phase); } return phase; } } }
再看一个示例,使用方式类似于Count为N的CountDownLatch:
public static void main(String[] args) { final Phaser phaser = new Phaser(); for(int i=0;i<10;i++){ phaser.register(); new Thread(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(new Random().nextInt(5)); System.out.println(Thread.currentThread() +" is ready!"); phaser.arriveAndAwaitAdvance(); System.out.println(Thread.currentThread() +" start!!!!"); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }
继续看一个示例,和第一个示例差不多,但有一点区别:第一个示例中线程只执行了一次逻辑,现在要求线程执行若干次逻辑:
public static void main(String[] args) { final Phaser phaser = new Phaser(1){ protected boolean onAdvance(int phase, int registeredParties) { System.out.println("now phase is " + phase); return phase >= 5 || registeredParties == 0; } }; for(int i=0;i<5;i++){ phaser.register(); final int time = i; new Thread(new Runnable() { @Override public void run() { do{ phaser.arriveAndAwaitAdvance(); System.out.println(Thread.currentThread() +" start-"+time+"!!!!"); }while(!phaser.isTerminated()); } }).start(); } try { TimeUnit.SECONDS.sleep(5); System.out.println("arriveAndDeregister..."); phaser.arriveAndDeregister(); } catch (InterruptedException e) { e.printStackTrace(); } }
示例输出如下:
arriveAndDeregister... now phase is 0 Thread[Thread-4,5,main] start-4!!!! Thread[Thread-1,5,main] start-1!!!! Thread[Thread-0,5,main] start-0!!!! Thread[Thread-2,5,main] start-2!!!! Thread[Thread-3,5,main] start-3!!!! now phase is 1 Thread[Thread-0,5,main] start-0!!!! Thread[Thread-4,5,main] start-4!!!! Thread[Thread-2,5,main] start-2!!!! Thread[Thread-3,5,main] start-3!!!! Thread[Thread-1,5,main] start-1!!!! now phase is 2 Thread[Thread-1,5,main] start-1!!!! Thread[Thread-3,5,main] start-3!!!! Thread[Thread-2,5,main] start-2!!!! Thread[Thread-4,5,main] start-4!!!! Thread[Thread-0,5,main] start-0!!!! now phase is 3 Thread[Thread-0,5,main] start-0!!!! Thread[Thread-3,5,main] start-3!!!! Thread[Thread-1,5,main] start-1!!!! Thread[Thread-2,5,main] start-2!!!! Thread[Thread-4,5,main] start-4!!!! now phase is 4 Thread[Thread-4,5,main] start-4!!!! Thread[Thread-0,5,main] start-0!!!! Thread[Thread-1,5,main] start-1!!!! Thread[Thread-3,5,main] start-3!!!! Thread[Thread-2,5,main] start-2!!!! now phase is 5 Thread[Thread-2,5,main] start-2!!!! Thread[Thread-3,5,main] start-3!!!! Thread[Thread-4,5,main] start-4!!!! Thread[Thread-0,5,main] start-0!!!! Thread[Thread-1,5,main] start-1!!!!
- 最后,看一下示例中未涉及到的代码。
private static int unarrivedOf(long s) { int counts = (int)s; return (counts == EMPTY) ? 0 : counts & UNARRIVED_MASK; } private static int partiesOf(long s) { return (int)s >>> PARTIES_SHIFT; } private static int phaseOf(long s) { return (int)(s >>> PHASE_SHIFT); } private static int arrivedOf(long s) { int counts = (int)s; return (counts == EMPTY) ? 0 : (counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK); }
这组方法可以从主状态中获取各个状态,是一组"拆包"方法。在具体功能方法内部可能不会调用这些方法,而是手工内联进去。
private String badArrive(long s) { return "Attempted arrival of unregistered party for " + stateToString(s); } private String badRegister(long s) { return "Attempt to register more than " + MAX_PARTIES + " parties for " + stateToString(s); } private String stateToString(long s) { return super.toString() + "[phase = " + phaseOf(s) + " parties = " + partiesOf(s) + " arrived = " + arrivedOf(s) + "]"; }
这badArrice和badRegister方法用于在逻辑中出现非法参与注册情况时,提供提示消息。stateToString方法将state转化为易懂的字符串形式,用于支持前面两个方法和toString方法。
public int bulkRegister(int parties) { if (parties < 0) throw new IllegalArgumentException(); if (parties == 0) return getPhase(); return doRegister(parties); } public final int getPhase() { return (int)(root.state >>> PHASE_SHIFT); }
相对于register的批量注册方法。
public int arrive() { return doArrive(false); }
和arriveAndDeregister类似,只是不会注销当前参与者。
public int awaitAdvance(int phase) { final Phaser root = this.root; long s = (root == this) ? state : reconcileState(); int p = (int)(s >>> PHASE_SHIFT); if (phase < 0) return phase; if (p == phase) return root.internalAwaitAdvance(phase, null); return p; }
等待phase值表示的阶段,如果当前phase和给定的phase不一致,直接返回当前的phase值。
public int awaitAdvanceInterruptibly(int phase) throws InterruptedException { final Phaser root = this.root; long s = (root == this) ? state : reconcileState(); int p = (int)(s >>> PHASE_SHIFT); if (phase < 0) return phase; if (p == phase) { QNode node = new QNode(this, phase, true, false, 0L); p = root.internalAwaitAdvance(phase, node); if (node.wasInterrupted) throw new InterruptedException(); } return p; }
awaitAdvance方法逻辑一样,但支持中断。
public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { long nanos = unit.toNanos(timeout); final Phaser root = this.root; long s = (root == this) ? state : reconcileState(); int p = (int)(s >>> PHASE_SHIFT); if (phase < 0) return phase; if (p == phase) { QNode node = new QNode(this, phase, true, true, nanos); p = root.internalAwaitAdvance(phase, node); if (node.wasInterrupted) throw new InterruptedException(); else if (p == phase) throw new TimeoutException(); } return p; }
awaitAdvance方法逻辑一样,但支持中断和超时。
public void forceTermination() { // Only need to change root state final Phaser root = this.root; long s; while ((s = root.state) >= 0) { //给主状态中添加结束标记。 if (UNSAFE.compareAndSwapLong(root, stateOffset, s, s | TERMINATION_BIT)) { //清空奇偶线程等待队列。 releaseWaiters(0); releaseWaiters(1); return; } } }
最后看一些支持监控的方法:
public final int getPhase() { return (int)(root.state >>> PHASE_SHIFT); } public int getRegisteredParties() { return partiesOf(state); } public int getArrivedParties() { return arrivedOf(reconcileState()); } public int getUnarrivedParties() { return unarrivedOf(reconcileState()); } public Phaser getParent() { return parent; } public Phaser getRoot() { return root; } public boolean isTerminated() { return root.state < 0L; }
OK,JDK1.7 Phaser的代码解析完毕!
参见:Jdk1.6 JUC源码解析(9)-CountDownLatch
参见:Jdk1.6 JUC源码解析(11)-CyclicBarrier
参见:Jdk1.7 JUC源码增量解析(5)-ForkJoin-ForkJoin框架其他过程及方法
相关推荐
在给定的压缩包文件"jdk1.7官网 jdk-7u80-linux-x64.tar.gz.zip"中,包含的主要内容是JDK 7u80的Linux 64位版本。这个版本是针对64位Linux操作系统的,确保在该环境下能够顺利进行Java开发工作。文件名"jdk-7u80-...
1. 首先,下载名为“jdk-7u79-linux-x64.tar.gz”的压缩文件,这是针对Linux 64位系统的JDK 1.7的归档文件。 2. 使用解压命令(如tar -zxvf jdk-7u79-linux-x64.tar.gz)将内容解压到指定目录。 3. 设置环境变量,...
安装Java JDK 1.7 on Windows x64的步骤非常简单,只需双击下载的“jdk-7u80-windows-x64.exe”文件,然后按照安装向导进行操作。安装过程中,记得选择合适的安装路径,并勾选“添加Java到系统环境变量”选项,以便...
4部分: jdk-1.7-windows-32-1 jdk-1.7-windows-32-2 jdk-1.7-windows-32-3 jdk-1.7-windows-32-4
三部分: jdk-1.7-windows-64-01 jdk-1.7-windows-64-02 jdk-1.7-windows-64-03
jdk1.7 64位官方正式版 jdk-7u71-macosx-x64.dmg
压缩包中的文件`[www.java1234.com]jdk-7u67-windows-x64.exe`是JDK 1.7的64位Windows版安装程序。安装这个版本的JDK后,开发者可以在Windows操作系统上进行Java 7的开发工作,包括编写、编译和运行Java程序,以及...
三部分: jdk-1.7-linux-32-1 jdk-1.7-linux-32-2 jdk-1.7-linux-32-3
三部分: jdk-1.7-linux-64-01 jdk-1.7-linux-64-02 jdk-1.7-linux-64-03
- 解压下载的`jdk-7u79-linux-x64`压缩包到适当目录。 - 配置环境变量`JAVA_HOME`,`PATH`和`CLASSPATH`,使系统能够找到JDK。 - 使用`java -version`命令检查安装是否成功。 5. **开发和运行Java程序**: - ...
标题"jdk1.7 64位 Linux版 jdk-7u79-linux-x64.tar.gz"明确指出我们讨论的是Java Development Kit (JDK) 的1.7版本,专为64位Linux操作系统设计。文件名"jdk-7u79-linux-x64.tar.gz"表明这是一个压缩文件,采用tar...
Java JDK 1.7源码包是用于在CentOS 7操作系统上进行OpenJDK 1.8编译的重要资源。这个源码包包含了Java Development Kit的1.7版本,通常被称为JDK 7,它是Oracle公司发布的Java编程语言和Java平台标准版的一个实现。...
文件比较大,给了百度云连接,直接下载 JDK1.7 64位 官方正版 jdk-7u80-macosx-x64.dmg 官方网站版本 苹果64位操作系统JDK
三部分: jdk-1.7-linux-64-01 jdk-1.7-linux-64-02 jdk-1.7-linux-64-03
jdk1.7 官方正式版32位下载 JDK详细介绍 JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK)。 SE(J2SE),standard edition,标准版,是我们通常用的一个版本,从JDK 5.0开始,改名为Java SE。 EE(J2EE)...
标题 "springboot+mybatis+jdk1.7" 指的是一个基于Spring Boot、MyBatis框架,并使用Java Development Kit 1.7版本构建的Web应用项目。这个项目已经搭建完成,具备基本的结构,方便开发者快速启动一个新的Java Web...
4部分: jdk-1.7-windows-32-1 jdk-1.7-windows-32-2 jdk-1.7-windows-32-3 jdk-1.7-windows-32-4
标题中的"jdk-1.7-java-7-openjdk-amd64.zip"表明这是一个Java开发工具包(JDK)的压缩文件,版本为1.7,适用于AMD64架构的Linux系统。OpenJDK是Java Development Kit的一个开源实现,由Oracle公司支持并维护。这个...
jdk-7u79-windows-i586.exe JDK7 稳定版 源官方下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html