入门Java线程基础一篇就够了

目录
  • 一、线程初步认识
    • 1、什么是线程
    • 2、Java本身就是多线程
    • 3、为什么要使用多线程
    • 4、线程的优先级
    • 5、线程的状态
    • 6、Daemon线程
  • 二、线程启动和终止
    • 1、构造线程
    • 2、什么是线程中断
    • 3、suspend()、resume()和stop()
    • 4、正确的终止线程
  • 总结:

简介

线程是操作系统调度的最小单元,在多核环境中,多个线程能同时执行,如果运用得当,能显著的提升程序的性能。

一、线程初步认识

1、什么是线程

操作系统运行一个程序会为其启动一个进程。例如,启动一个Java程序会创建一个Java进程。现代操作系统调度的最小单元是线程,线程也称为轻量级进程(Light Weight Process),一个进程中可以创建一个到多个线程,线程拥有自己的计数器、堆栈和局部变量等属性,并且能访问共享的内存变量。处理器会通过快速切换这些线程,来执行程序。 ​

2、Java本身就是多线程

示例代码:

package com.lizba.p2;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
/**
 * <p>
 *
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/13 23:03
 */
public class MultiThread {
    public static void main(String[] args) {
        // 获取Java线程管理MXBean
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        // 获取线程和线程堆栈信息;
        // boolean lockedMonitors = false  不需要获取同步的monitor信息;
        // boolean lockedSynchronizers = false  不需要获取同步的synchronizer信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
        // 打印线程ID和线程name
        Arrays.stream(threadInfos).forEach(threadInfo -> {
            System.out.println("[" + threadInfo.getThreadId() + "]" + threadInfo.getThreadName());
        });
    }
}

输出结果(不一定一致):

[6]Monitor Ctrl-Break // idea中特有的线程(不用管)

[5]Attach Listener // JVM进程间的通信线程

[4]Signal Dispatcher // 分发处理发送给JVM信号的线程

[3]Finalizer // 调用对象的finalizer线程

[2]Reference Handler // 清楚Reference的线程

[1]main // main线程,用户程序入口

总结:从输出结果不难看出,Java程序本身就是多线程的。它不仅仅只有一个main线程在运行,而是main线程和其他多个线程在同时运行。

3、为什么要使用多线程

使用多线程的好处如下:

1.更多处理器核心

计算机处理器核心数增多,由以前的高主频向多核心技术发展,现在的计算机更擅长于并行计算,因此如何充分利用多核心处理器是现在的主要问题。线程是操作系统调度的最小单元,一个程序作为一个进程来运行,它会创建多个线程,而一个线程在同一时刻只能运行在一个处理器上。因此一个进程如果能使用多线程计算,将其计算逻辑分配到多个处理器核心上,那么相比单线程运行将会有更显著的性能提升。

2.更快响应时间

在复杂业务场景中,我们可以将非强一致性关联的业务派发给其他线程处理(或者使用消息队列)。这样可以减少应用响应用户请求的时间

3.更好的编程模型

合理使用Java的提供的多线程编程模型,能使得程序员更好的解决问题,而不需要过于复杂的考虑如何将其多线程化。

4、线程的优先级

现代操作系统基本采用的是时间片分配的方式来调度线程,也就是操作系统将CPU的运行分为一个个时间片,线程会分配的若干时间片,当线程时间片用完了,就会发生线程调度等待下次时间片的分配。线程在一次CPU调度中能执行多久,取决于所分时间片的多少,而线程优先级就是决定线程需要多或者少分配一些处理器资源的线程属性。在Java线程中,线程的优先级的可设置范围是1-10,默认优先级是5,理论上优先级高的线程分配时间片数量要优先于低的线程(部分操作系统这个设置是不生效的); ​

示例代码

package com.lizba.p2;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * <p>
 *      线程优先级设置
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 12:03
 */
public class Priority {
    /** 线程执行流程控制开关 */
    private static volatile boolean notStart = true;
    /** 线程执行流程控制开关 */
    private static volatile boolean notEnd = true;
    public static void main(String[] args) throws InterruptedException {
        List<Job> jobs = new ArrayList<>();
        // 设置5个优先级为1的线程,设置5个优先级为10的线程
        for (int i = 0; i < 10; i++) {
            int priority = i < 5 ? Thread.MIN_PRIORITY : Thread.MAX_PRIORITY;
            Job job = new Job(priority);
            jobs.add(job);
            Thread thread = new Thread(job, "Thread:" + i);
            thread.setPriority(priority);
            thread.start();
        }
        notStart = false;
        TimeUnit.SECONDS.sleep(10);
        notEnd = false;
        jobs.forEach(
                job -> System.out.println("Job priority : " + job.priority + ", Count : " + job.jobCount)
        );
    }

    /**
     * 通过Job来记录线程的执行次数和优先级
     */
    static class Job implements Runnable {
        private int priority;
        private long jobCount;
        public Job(int priority) {
            this.priority = priority;
        }
        @Override
        public void run() {
            while (notStart) {
                // 让出CPU时间片,等待下次调度
                Thread.yield();
            }
            while (notEnd) {
                // 让出CPU时间片,等待下次调度
                Thread.yield();
                jobCount++;
            }
        }
    }
}

执行结果

从输出结果上来看,优先级为1的线程和优先级为10的线程执行的次数非常相近,因此这表明程序正确性是不能依赖线程的优先级高低的。

5、线程的状态

线程的生命周期如下:

状态名称 说明
NEW 初始状态,线程被构建,并未调用start()方法
RUNNABLE 运行状态,Java线程将操作系统中的就绪和运行两种状态统称为“运行中”
BLOCKED 阻塞状态,线程阻塞于锁
WAITING 等待状态,线程进入等待状态,进入该状态表示当前线程需要等待其他线程作出一些特定动作(通知或中断)
TIME_WAITING 超时等待,先比WAITING可以在指定的时间内自行返回
TERMINATED 终止状态,表示当前线程已经执行完毕

通过代码来查看Java线程的状态

代码示例:

package com.lizba.p2;
import java.util.concurrent.TimeUnit;
/**
 * <p>
 *      睡眠指定时间工工具类
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 13:27
 */
public class SleepUtil {
    public static final void sleepSecond(long seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
package com.lizba.p2;
/**
 * <p>
 *      线程状态示例代码
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 13:25
 */
public class ThreadStateDemo {
    public static void main(String[] args) {
        // TimeWaiting
        new Thread(new TimeWaiting(), "TimeWaitingThread").start();
        // Waiting
        new Thread(new Waiting(), "WaitingThread").start();
        // Blocked1和Blocked2一个获取锁成功,一个获取失败
        new Thread(new Blocked(), "Blocked1Thread").start();
        new Thread(new Blocked(), "Blocked2Thread").start();
    }
    // 线程不断的进行睡眠
    static class TimeWaiting implements Runnable {
        @Override
        public void run() {
            while (true) {
                SleepUtil.sleepSecond(100);
            }
        }
    }
    // 线程等待在Waiting.class实例上
    static class Waiting implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (Waiting.class) {
                    try {
                        Waiting.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    // 该线程Blocked.class实例上加锁,不会释放该锁
    static class Blocked implements Runnable {
        @Override
        public void run() {
            synchronized (Blocked.class) {
                while (true) {
                    SleepUtil.sleepSecond(100);
                }
            }
        }
    }
}

使用JPS查看Java进程

查看示例代码ThreadStateDemo进程ID是2576,键入jstack 2576查看输出:

整理输出结果

线程名称 线程状态
Blocked2Thread BLOCKED (on object monitor),阻塞在获取Blocked.class的锁上
Blocked1Thread TIMED_WAITING (sleeping)
WaitingThread WAITING (on object monitor)
TimeWaitingThread TIMED_WAITING (sleeping)

总结:线程在自身生命周期中不是规定处于某一个状态,而是随着代码的执行在不同的状态之间进行切换。 ​

Java线程的状态变化图如下

Java线程状态变迁图

总结:

  • 线程创建后,调用start()方法开始运行
  • 线程执行wait()方法后,线程进入等待状态,进入等待的线程需要依靠其他线程才能够返回到运行状态
  • 超时等待相当于在等待状态的基础上增加了超时限制,达到设置的超时时间后返回到运行状态
  • 线程执行同步方法或代码块时,未获取到锁的线程,将会进入到阻塞状态。
  • 线程执行完Runnable的run()方法之后进入到终止状态
  • 阻塞在Java的concurrent包中Lock接口的线程是等待状态,因为Lock接口阻塞的实现使用的是Daemon线程

6、Daemon线程

简介:

Daemon线程是一种支持型线程,它的主要作用是程序中后台调度和支持性工作。当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。Daemon线程需要在启动之前设置,不能在启动之后设置。

设置方式

Thread.setDaemon(true)

需要特别注意的点

Daemon线程被用作支持性工作的完成,但是在Java虚拟机退出时Daemon线程的finally代码块不一定执行。

示例代码

package com.lizba.p2;
/**
 * <p>
 *      DaemonRunner线程
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 19:50
 */
public class DaemonRunner implements Runnable{
    @Override
    public void run() {
        try {
            SleepUtil.sleepSecond(100);
        } finally {
            System.out.println("DaemonRunner finally run ...");
        }
    }
}

测试

package com.lizba.p2;
/**
 * <p>
 *
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 19:59
 */
public class DaemonTest {
    public static void main(String[] args) {
        Thread t = new Thread(new DaemonRunner(), "DaemonRunner");
        t.setDaemon(true);
        t.start();
    }
}

输出结果

总结

不难发现,DaemonRunner的run方法的finally代码块并没有执行,这是因为,当Java虚拟机中已经没有非Daemon线程时,虚拟机会立即退出,虚拟机中的所以daemon线程需要立即终止,所以线程DaemonRunner会被立即终止,finally并未执行。

二、线程启动和终止

1、构造线程

运行线程之前需要构造一个线程对象,线程对象在构造的时候需要设置一些线程的属性,这些属性包括线程组、线程的优先级、是否是daemon线程、线程名称等信息。

代码示例:

来自java.lang.Thread

  /**     * Initializes a Thread.     *     * @param g the Thread group     * @param target the object whose run() method gets called     * @param name the name of the new Thread     * @param stackSize the desired stack size for the new thread, or     *        zero to indicate that this parameter is to be ignored.     * @param acc the AccessControlContext to inherit, or     *            AccessController.getContext() if null     * @param inheritThreadLocals if {@code true}, inherit initial values for     *            inheritable thread-locals from the constructing thread     */    private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {        if (name == null) {            throw new NullPointerException("name cannot be null");        }// 设置线程名称        this.name = name;// 当前线程设置为该线程的父线程        Thread parent = currentThread();        SecurityManager security = System.getSecurityManager();        if (g == null) {            if (security != null) {                g = security.getThreadGroup();            }            if (g == null) {                g = parent.getThreadGroup();            }        }        g.checkAccess();        if (security != null) {            if (isCCLOverridden(getClass())) {                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);            }        }        g.addUnstarted();// 设置线程组        this.group = g;        // 将daemon属性设置为父线程的对应的属性        this.daemon = parent.isDaemon();        // 将prority属性设置为父线程的对应的属性        this.priority = parent.getPriority();        if (security == null || isCCLOverridden(parent.getClass()))            this.contextClassLoader = parent.getContextClassLoader();        else            this.contextClassLoader = parent.contextClassLoader;        this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();        this.target = target;        setPriority(priority);        // 复制父线程的InheritableThreadLocals属性        if (inheritThreadLocals && parent.inheritableThreadLocals != null)            this.inheritableThreadLocals =                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);        /* Stash the specified stack size in case the VM cares */        this.stackSize = stackSize;       // 设置一个线程id        tid = nextThreadID();    }

总结

在上述代码中,一个新构建的线程对象时由其parent线程来分配空间的,而child继承了parent是否为Daemon、优先级和加载资源的contextClassLoader以及可继承的ThreadLocal,同时会分配一个唯一的ID来标志线程。此时一个完整的能够运行的线程对象就初始化好了,在堆内存中等待运行。 ​

2、什么是线程中断

中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。线程通过检查自身是否被中断来进行响应,线程通过方法isInterrupted()来进行判断是否被中断,也可以通过调用静态方法Thread.interrupted()对当前线程的中断标志位进行复位。如下情况不能准确判断线程是否被中断过:

线程已经终止运行,即使被中断过,isInterrupted()方法也会返回false方法抛出InterruptedException异常,即使被中断过,调用isInterrupted()方法将会返回false,这是因为抛出InterruptedException之前会清除中断标志。

示例代码:

package com.lizba.p2;
/**
 * <p>
 *      线程中断示例代码
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 20:36
 */
public class Interrupted {
    public static void main(String[] args) {
        // sleepThread不停的尝试睡眠
        Thread sleepThread = new Thread(new SleepRunner(), "sleepThread");
        sleepThread.setDaemon(true);
        // busyThread
        Thread busyThread = new Thread(new BusyRunner(), "busyThread");
        busyThread.setDaemon(true);
        // 启动两个线程
        sleepThread.start();
        busyThread.start();
        // 休眠5秒,让sleepThread和busyThread运行充分
        SleepUtil.sleepSecond(5);
        // 中断两个线程
        sleepThread.interrupt();
        busyThread.interrupt();
        System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());
        System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());
        // 睡眠主线程,防止daemon线程退出
        SleepUtil.sleepSecond(2);
    }
    static class SleepRunner implements Runnable {
        @Override
        public void run() {
            while (true) {
                SleepUtil.sleepSecond(10);
            }
        }
    }
    static class BusyRunner implements Runnable {
        @Override
        public void run() {
            while (true) {}
        }
    }
}

查看运行结果:

总结

抛出InterruptedException的是sleepThread线程,虽然两者都被中断过,但是sleepThread线程的中断标志返回的是false,这是因为TimeUnit.SECONDS.sleep(seconds)会抛出InterruptedException异常,抛出异常之前,sleepThread线程的中断标志被清除了。但是,busyThread一直在运行没有抛出异常,中断位没有被清除。 ​

3、suspend()、resume()和stop()

举例:

线程这三个方法,相当于QQ音乐播放音乐时的暂停、恢复和停止操作。(注意这些方法已经过期了,不建议使用。)

示例代码

package com.lizba.p2;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
 * <p>
 *      线程过期方法示例
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 20:57
 */
public class Deprecated {

    static DateFormat format = new SimpleDateFormat("HH:mm:ss");
    public static void main(String[] args) {
        Thread printThread = new Thread(new PrintThread(), "PrintThread");
        printThread.start();
        SleepUtil.sleepSecond(3);
        // 暂停printThread输出
        printThread.suspend();
        System.out.println("main suspend PrintThread at " + format.format(new Date()));
        SleepUtil.sleepSecond(3);
        // 恢复printThread输出
        printThread.resume();
        System.out.println("main resume PrintThread at " + format.format(new Date()));
        SleepUtil.sleepSecond(3);
        // 终止printThread输出
        printThread.stop();
        System.out.println("main stop PrintThread at " + format.format(new Date()));
        SleepUtil.sleepSecond(3);
    }

    static class PrintThread implements Runnable {
        @Override
        public void run() {
           while (true) {
               System.out.println(Thread.currentThread().getName() + "Run at "
               + format.format(new Date()));
               SleepUtil.sleepSecond(1);
           }
        }
    }
}

输出结果

总结

上述代码执行输出的结果,与API说明和我们的预期完成一致,但是看似正确的代码却隐藏这很多问题。

存在问题

  • suspend()方法调用后不会释放已占有的资源(比如锁),可能会导致死锁
  • stop()方法在终结一个线程时不能保证资源的正常释放,可能会导致程序处于不确定的工作状态

4、正确的终止线程

调用线程的interrupt()方法使用一个Boolean类型的变量来控制是否停止任务并终止线程

示例代码

package com.lizba.p2;
/**
 * <p>
 *      标志位终止线程示例代码
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/6/14 21:17
 */
public class ShutDown {
    public static void main(String[] args) {
        Runner one = new Runner();
        Thread t = new Thread(one, "CountThread");
        t.start();
        SleepUtil.sleepSecond(1);
        t.interrupt();
        Runner two = new Runner();
        t = new Thread(two, "CountThread");
        t.start();
        SleepUtil.sleepSecond(1);
        two.cancel();
    }

    private static class Runner implements Runnable {
        private long i;
        private volatile boolean on = true;
        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("Count i = " +i);
        }
        /**
         * 关闭
         */
        public void cancel() {
            on = false;
        }
    }
}

输出结果:

总结:

main线程通过中断操作和cancel()方法均可使CountThread得以终止。这两种方法终止线程的好处是能让线程在终止时有机会去清理资源。做法更加安全和优雅。希望大家可以多多关注我们的更多内容! ​

(0)

相关推荐

  • Java 高并发二:多线程基础详细介绍

    本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍 1.什么是线程 2.线程的基本操作 3.守护线程 4.线程优先级 5.基本的线程同步操作 1. 什么是线程 线程是进程内的执行单元 某个进程当中都有若干个线程. 线程是进程内的执行单元. 使用线程的原因是,进程的切换是非常重量级的操作,非常消耗资源.如果使用多进程,那么并发数相对来说不会很高.而线程是更细小的调度单元,更加轻量级,所以线程会较为广泛的用于并发设计. 在Java当中线程的概念和操作系统级别线程的概念是类似的.事

  • Java多线程基础——Lock类

    之前已经说道,JVM提供了synchronized关键字来实现对变量的同步访问以及用wait和notify来实现线程间通信.在jdk1.5以后,JAVA提供了Lock类来实现和synchronized一样的功能,并且还提供了Condition来显示线程间通信. Lock类是Java类来提供的功能,丰富的api使得Lock类的同步功能比synchronized的同步更强大.本文章的所有代码均在Lock类例子的代码 本文主要介绍一下内容: Lock类 Lock类其他功能 Condition类 Con

  • java并发编程专题(一)----线程基础知识

    在任何的生产环境中我们都不可逃避并发这个问题,多线程作为并发问题的技术支持让我们不得不去了解.这一块知识就像一个大蛋糕一样等着我们去分享,抱着学习的心态,记录下自己对并发的认识. 1.线程的状态: 线程状态图: 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权. 3.运行状态(Running):就绪状态的线程获取了CPU,执行程序代码. 4

  • Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll)

    本篇我们来研究一下 wait() notify() notifyAll() . DEMO1: wait() 与 notify() public class Test { static class ThreadOne extends Thread { private Callback mCallback; @Override public void run() { work(); if (mCallback != null) { mCallback.onResult(false); } } //

  • 入门Java线程基础一篇就够了

    目录 一.线程初步认识 1.什么是线程 2.Java本身就是多线程 3.为什么要使用多线程 4.线程的优先级 5.线程的状态 6.Daemon线程 二.线程启动和终止 1.构造线程 2.什么是线程中断 3.suspend().resume()和stop() 4.正确的终止线程 总结: 简介: 线程是操作系统调度的最小单元,在多核环境中,多个线程能同时执行,如果运用得当,能显著的提升程序的性能. 一.线程初步认识 1.什么是线程 操作系统运行一个程序会为其启动一个进程.例如,启动一个Java程序会

  • java多线程从入门到精通看这篇就够了

    目录 一.认识线程及线程的创建 1.线程的概念 2.线程的特性 3.线程的创建方式 <1>继承Thread类 <2>实现Runnable接口 <3>实现Callable接口 二.线程的常用方法 1.构造方法和属性的获取方法 2.常用方法 <1>run()和start() <2>interrupt()方法 <3>join方法 <4>获取当前线程的引用currentThread();方法 <5>休眠当前线程slee

  • Java数组看这篇就够了

    目录 一.前言 二.数组的定义数组定义的形式: 三.数组的初始化方式: 1.动态初始化 2.静态初始化 四.索引访问数组 五.数组长度表示 六.遍历数组 方法一: 方法二:实例演示: 七.数组的初始值 总结 一.前言 前面我们学习了随机数的介绍和使用,那么这篇我们来学习java中数组的定义和使用, java的数组和c语言的十分类似. 二.数组的定义数组定义的形式: 格式1: 数据类型 [ ] 数组名 :如int [ ]arr; 说明:定义了一个int类型的数组,数组名是arr 格式2: 数据类型

  • 快速入门ASP.NET Core看这篇就够了

    本来这篇只是想简单介绍下ASP.NET Core MVC项目的(毕竟要照顾到很多新手朋友),但是转念一想不如来点猛的(考虑到急性子的朋友),让你通过本文的学习就能快速的入门ASP.NET Core.既然是快速入门所以过多过深的内容我这里就一笔带过了!然后在后面的一些列文章中再慢慢的对其中的概念进行阐述. .NET Core是什么 很多朋友看到.NET Core就认为是ASP.NET Core,其实这是有误区的,因为.NET Core 是开放源代码的通用开发平台 (是一个"平台"),基于

  • Java-lambda表达式入门看这一篇就够了

    概述 Lambda表达式,也可称为闭包,是JDK8的新特性.Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中),可以使代码变的更加简洁紧凑.Lambda表达式是一个可传递的代码块,可以在以后执行一次或多次. 名字起源是以前还没有计算机时,逻辑学家Alonzo Church想要形式化的表示能有效计算的数学函数,使用了希腊字母lambda( λ \lambda λ)来标记参数,从那以后,带参数变量的表达式就被称为lambda表达式. lambda表达式本质是一个匿名函数,比如以下

  • java新人基础入门之递归调用

    一.递归概念 递归本质:程序调用自身的编程技巧叫做递归. 程序调用自身的编程技巧称为递归( recursion).递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调: 用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过: 程所需要的多次重复计算,大大地减少了程序的代码量.递归的能力在于用有限的语句来定义对象的无限集合. 二.递归的三个条件: 边界条件 递归前进段 递归返回段 当

  • Java零基础入门数组

    目录 认识数组 数组的初始化 默认初始化 静态初始化 动态初始化 数组有关的问题 数组的遍历 求最值问题 查询子元素 Arrays工具类 二维数组 二维数组初始方式 二维数组的遍历 认识数组 数组的定义 数组是相同类型数据的有序集合.数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成.其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们. 数组的四个基本特点 长度是确定的.数组一旦被创建,它的大小就是不可以改变的. 其元素的类型必须是相同类型,不允许出现混合类型

  • Java线程安全基础概念解析

    Java线程安全初步了解.JAVA线程安全从总体上来说,是指Java对象在多线程运行环境下的一种特性,表现为常规(区别于特殊调用情况)情况下每次调用都能得到正确的逻辑结果.从本质上来说,将对象的方法行为加上了同步控制逻辑,而调用者无须做其他额外的同步控制就可以安全放心的使用对象. 1.线程安全的定义 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安

  • Java web基础学习之开发环境篇(详解)

    Tomcat + Eclipse添加Java EE插件 因为之前进行Java SE学习已经配置了JDK,安装了Eclipse for Java SE,所以选择了在Eclipse上添加插件的方式来配置Web开发环境 Tomcat是免安装版,直接解压即可: Eclipse中"帮助-安装新软件",work with处选择Mars - http://download.eclipse.org/releases/mars(注意对应自己版本): 选择Web.Java EE那个选项进行安装即可,如果报

随机推荐