多线程基础必要知识点!看了学习多线程事半功倍

Java实现多线程下载源代码 这里面有用Java实现的多线程下载的源程序,且是用NetBeans写的用户界面,方便大家阅读程序。 立即下载

前言

不小心就鸽了几天没有更新了,这个星期回家咯。在学校的日子要努力一点才行!

只有光头才能变强

回顾前面:

本文章的知识主要参考《Java并发编程实战》这本书的前4章,这本书的前4章都是讲解并发的基础的。要是能好好理解这些基础,那么我们往后的学习就会事半功倍。

当然了,《Java并发编程实战》可以说是非常经典的一本书。我是未能完全理解的,在这也仅仅是抛砖引玉。想要更加全面地理解我下面所说的知识点,可以去阅读一下这本书,总的来说还是不错的。

首先来预览一下《Java并发编程实战》前4章的目录究竟在讲什么吧:

第1章 简介

  • 1.1 并发简史
  • 1.2 线程的优势
  • 1.2.1 发挥多处理器的强大能力
  • 1.2.2 建模的简单性
  • 1.2.3 异步事件的简化处理
  • 1.2.4 响应更灵敏的用户界面
  • 1.3 线程带来的风险
  • 1.3.1 安全性问题
  • 1.3.2 活跃性问题
  • 1.3.3 性能问题
  • 1.4 线程无处不在

ps:这一部分我就不讲了,主要是引出我们接下来的知识点,有兴趣的同学可翻看原书~

第2章 线程安全性

  • 2.1 什么是线程安全性
  • 2.2 原子性
  • 2.2.1 竞态条件
  • 2.2.2 示例:延迟初始化中的竞态条件
  • 2.2.3 复合操作
  • 2.3 加锁机制
  • 2.3.1 内置锁
  • 2.3.2 重入
  • 2.4 用锁来保护状态
  • 2.5 活跃性与性能

第3章 对象的共享

  • 3.1 可见性
  • 3.1.1 失效数据
  • 3.1.2 非原子的64位操作
  • 3.1.3 加锁与可见性
  • 3.1.4 Volatile变量
  • 3.2 发布与逸出
  • 3.3 线程封闭
  • 3.3.1 Ad-hoc线程封闭
  • 3.3.2 栈封闭
  • 3.3.3 ThreadLocal类
  • 3.4 不变性
  • 3.4.1 Final域
  • 3.4.2 示例:使用Volatile类型来发布不可变对象
  • 3.5 安全发布
  • 3.5.1 不正确的发布:正确的对象被破坏
  • 3.5.2  不可变对象与初始化安全性
  • 3.5.3 安全发布的常用模式
  • 3.5.4 事实不可变对象
  • 3.5.5 可变对象
  • 3.5.6 安全地共享对象

第4章 对象的组合

  • 4.1 设计线程安全的类
  • 4.1.1 收集同步需求
  • 4.1.2 依赖状态的操作
  • 4.1.3 状态的所有权
  • 4.2 实例封闭
  • 4.2.1 Java监视器模式
  • 4.2.2 示例:车辆追踪
  • 4.3 线程安全性的委托
  • 4.3.1 示例:基于委托的车辆追踪器
  • 4.3.2 独立的状态变量
  • 4.3.3 当委托失效时
  • 4.3.4 发布底层的状态变量
  • 4.3.5 示例:发布状态的车辆追踪器
  • 4.4 在现有的线程安全类中添加功能
  • 4.4.1 客户端加锁机制
  • 4.4.2 组合
  • 4.5 将同步策略文档化

那么接下来我们就开始吧~

一、使用多线程遇到的问题

1.1线程安全问题

在前面的文章中已经讲解了线程【多线程三分钟就可以入个门了!】,多线程主要是为了提高我们应用程序的使用率。但同时,这会给我们带来很多安全问题!

如果我们在单线程中以“顺序”(串行–>独占)的方式执行代码是没有任何问题的。但是到了多线程的环境下(并行),如果没有设计和控制得好,就会给我们带来很多意想不到的状况,也就是线程安全性问题

因为在多线程的环境下,线程是交替执行的,一般他们会使用多个线程执行相同的代码。如果在此相同的代码里边有着共享的变量,或者一些组合操作,我们想要的正确结果就很容易出现了问题

简单举个例子:

  • 下面的程序在单线程中跑起来,是没有问题的。

public class UnsafeCountingServlet extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        ++count;
        // To something else...
    }
}

但是在多线程环境下跑起来,它的count值计算就不对了!

首先,它共享了count这个变量,其次来说++count;这是一个组合的操作(注意,它并非是原子性)

  • ++count实际上的操作是这样子的:
    • 读取count值
    • 将值+1
    • 将计算结果写入count

于是多线程执行的时候很可能就会有这样的情况:

  • 当线程A读取到count的值是8的时候,同时线程B也进去这个方法上了,也是读取到count的值为8
  • 它俩都对值进行加1
  • 将计算结果写入到count上。但是,写入到count上的结果是9
  • 也就是说:两个线程进来了,但是正确的结果是应该返回10,而它返回了9,这是不正常的!

如果说:当多个线程访问某个类的时候,这个类始终能表现出正确的行为,那么这个类就是线程安全的!

有个原则:能使用JDK提供的线程安全机制,就使用JDK的。

当然了,此部分其实是我们学习多线程最重要的环节,这里我就不详细说了。这里只是一个总览,这些知识点在后面的学习中都会遇到~~~

1.3性能问题

使用多线程我们的目的就是为了提高应用程序的使用率,但是如果多线程的代码没有好好设计的话,那未必会提高效率。反而降低了效率,甚至会造成死锁!

就比如说我们的Servlet,一个Servlet对象可以处理多个请求的,Servlet显然是一个天然支持多线程的。

又以下面的例子来说吧:


public class UnsafeCountingServlet extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        ++count;
        // To something else...
    }
}

从上面我们已经说了,上面这个类是线程不安全的。最简单的方式:如果我们在service方法上加上JDK为我们提供的内置锁synchronized,那么我们就可以实现线程安全了。


public class UnsafeCountingServlet extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void synchronized service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        ++count;
        // To something else...
    }
}

虽然实现了线程安全了,但是这会带来很严重的性能问题:

  • 每个请求都得等待上一个请求的service方法处理了以后才可以完成对应的操作

这就导致了:我们完成一个小小的功能,使用了多线程的目的是想要提高效率,但现在没有把握得当,却带来严重的性能问题!

在使用多线程的时候:更严重的时候还有死锁(程序就卡住不动了)。

这些都是我们接下来要学习的地方:学习使用哪种同步机制来实现线程安全,并且性能是提高了而不是降低了~

二、对象的发布与逸出

书上是这样定义发布和逸出的:

发布(publish) 使对象能够在当前作用域之外的代码中使用

逸出(escape) 当某个不应该发布的对象被发布了

常见逸出的有下面几种方式:

  • 静态域逸出
  • public修饰的get方法
  • 方法参数传递
  • 隐式的this

静态域逸出:

public修饰get方法:

方法参数传递我就不再演示了,因为把对象传递过去给另外的方法,已经是逸出了~

下面来看看该书给出this逸出的例子:

逸出就是本不应该发布对象的地方,把对象发布了。导致我们的数据泄露出去了,这就造成了一个安全隐患!理解起来是不是简单了一丢丢?

2.1安全发布对象

上面谈到了好几种逸出的情况,我们接下来来谈谈如何安全发布对象。

安全发布对象有几种常见的方式:

  • 在静态域中直接初始化 : public static Person = new Person();
    • 静态初始化由JVM在类的初始化阶段就执行了,JVM内部存在着同步机制,致使这种方式我们可以安全发布对象
  • 对应的引用保存到volatile或者AtomicReferance引用中
    • 保证了该对象的引用的可见性和原子性
  • 由final修饰
    • 该对象是不可变的,那么线程就一定是安全的,所以是安全发布~
  • 由锁来保护
    • 发布和使用的时候都需要加锁,这样才保证能够该对象不会逸出

三、解决多线程遇到的问题

从上面我们就可以看到,使用多线程会把我们的系统搞得挺复杂的。是需要我们去处理很多事情,为了防止多线程给我们带来的安全和性能的问题~

下面就来简单总结一下我们需要哪些知识点来解决多线程遇到的问题。

3.1简述解决线程安全性的办法

使用多线程就一定要保证我们的线程是安全的,这是最重要的地方!

在Java中,我们一般会有下面这么几种办法来实现线程安全问题:

  • 无状态(没有共享变量)
  • 使用final使该引用变量不可变(如果该对象引用也引用了其他的对象,那么无论是发布或者使用时都需要加锁)
  • 加锁(内置锁,显示Lock锁)
  • 使用JDK为我们提供的类来实现线程安全(此部分的类就很多了)
    • 原子性(就比如上面的count++操作,可以使用AtomicLong来实现原子性,那么在增加的时候就不会出差错了!)
    • 容器(ConcurrentHashMap等等…)
    • ……
  • …等等

3.2原子性和可见性

何为原子性?何为可见性?当初我在ConcurrentHashMap基于JDK1.8源码剖析中已经简单说了一下了。不了解的同学可以进去看看。

3.2.1原子性

在多线程中很多时候都是因为某个操作不是原子性的,使数据混乱出错。如果操作的数据是原子性的,那么就可以很大程度上避免了线程安全问题了!

  • count++,先读取,后自增,再赋值。如果该操作是原子性的,那么就可以说线程安全了(因为没有中间的三部环节,一步到位【原子性】~

原子性就是执行某一个操作是不可分割的,
- 比如上面所说的count++操作,它就不是一个原子性的操作,它是分成了三个步骤的来实现这个操作的~
- JDK中有atomic包提供给我们实现原子性操作~

也有人将其做成了表格来分类,我们来看看:

图片来源:<a href=https://blog.youkuaiyun.com/eson_15/article/details/51553338” title=”” />

使用这些类相关的操作也可以进他的博客去看看:

3.2.2可见性

对于可见性,Java提供了一个关键字:volatile给我们使用~

  • 我们可以简单认为:volatile是一种轻量级的同步机制

volatile经典总结:volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性

我们将其拆开来解释一下:

  • 保证该变量对所有线程的可见性
    • 在多线程的环境下:当这个变量修改时,所有的线程都会知道该变量被修改了,也就是所谓的“可见性”
  • 不保证原子性
    • 修改变量(赋值)实质上是在JVM中分了好几步,而在这几步内(从装载变量到修改),它是不安全的。

使用了volatile修饰的变量保证了三点:

  • 一旦你完成写入,任何访问这个字段的线程将会得到最新的值
  • 在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。
  • volatile可以防止重排序(重排序指的就是:程序执行的时候,CPU、编译器可能会对执行顺序做一些调整,导致执行的顺序并不是从上往下的。从而出现了一些意想不到的效果)。而如果声明了volatile,那么CPU、编译器就会知道这个变量是共享的,不会被缓存在寄存器或者其他不可见的地方。

一般来说,volatile大多用于标志位上(判断操作),满足下面的条件才应该使用volatile修饰变量:

  • 修改变量时不依赖变量的当前值(因为volatile是不保证原子性的)
  • 该变量不会纳入到不变性条件中(该变量是可变的)
  • 在访问变量的时候不需要加锁(加锁就没必要使用volatile这种轻量级同步机制了)

参考资料:

3.3线程封闭

在多线程的环境下,只要我们不使用成员变量(不共享数据),那么就不会出现线程安全的问题了。

就用我们熟悉的Servlet来举例子,写了那么多的Servlet,你见过我们说要加锁吗??我们所有的数据都是在方法(栈封闭)上操作的,每个线程都拥有自己的变量,互不干扰!

在方法上操作,只要我们保证不要在栈(方法)上发布对象(每个变量的作用域仅仅停留在当前的方法上),那么我们的线程就是安全的

在线程封闭上还有另一种方法,就是我之前写过的:ThreadLocal就是这么简单

使用这个类的API就可以保证每个线程自己独占一个变量。(详情去读上面的文章即可)~

3.4不变性

不可变对象一定线程安全的。

上面我们共享的变量都是可变的,正由于是可变的才会出现线程安全问题。如果该状态是不可变的,那么随便多个线程访问都是没有问题的!

Java提供了final修饰符给我们使用,final的身影我们可能就见得比较多了,但值得说明的是:

  • final仅仅是不能修改该变量的引用,但是引用里边的数据是可以改的!

就好像下面这个HashMap,用final修饰了。但是它仅仅保证了该对象引用hashMap变量所指向是不可变的,但是hashMap内部的数据是可变的,也就是说:可以add,remove等等操作到集合中~~~

  • 因此,仅仅只能够说明hashMap是一个不可变的对象引用

  final HashMap<Person> hashMap = new HashMap<>();

不可变的对象引用在使用的时候还是需要加锁的

  • 或者把Person也设计成是一个线程安全的类~
  • 因为内部的状态是可变的,不加锁或者Person不是线程安全类,操作都是有危险的!

要想将对象设计成不可变对象,那么要满足下面三个条件:

  • 对象创建后状态就不能修改
  • 对象所有的域都是final修饰的
  • 对象是正确创建的(没有this引用逸出)

String在我们学习的过程中我们就知道它是一个不可变对象,但是它没有遵循第二点(对象所有的域都是final修饰的),因为JVM在内部做了优化的。但是我们如果是要自己设计不可变对象,是需要满足三个条件的。

3.5线程安全性委托

很多时候我们要实现线程安全未必就需要自己加锁,自己来设计。

我们可以使用JDK给我们提供的对象来完成线程安全的设计:

非常多的”工具类”供我们使用,这些在往后的学习中都会有所介绍的~~这里就不介绍了

四、最后

正确使用多线程能够提高我们应用程序的效率,同时给我们会带来非常多的问题,这些都是我们在使用多线程之前需要注意的地方。

无论是不变性、可见性、原子性、线程封闭、委托这些都是实现线程安全的一种手段。要合理地使用这些手段,我们的程序才可以更加健壮!

可以发现的是,上面在很多的地方说到了:锁。但我没有介绍它,因为我打算留在下一篇来写,敬请期待~~~

书上前4章花了65页来讲解,而我只用了一篇文章来概括,这是远远不够的,想要继续深入的同学可以去阅读书籍~

之前在学习操作系统的时候根据《计算机操作系统-汤小丹》这本书也做了一点点笔记,都是比较浅显的知识点。或许对大家有帮助

参考资料:

  • 《Java核心技术卷一》
  • 《Java并发编程实战》
  • 《计算机操作系统-汤小丹》

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航:

java多线程设计模式及源码 多线程与并发处理是程序设计好坏优劣的重要课题,本书通过浅显易懂的文字与实例来介绍JAVA线程相关的设计模式概念,并且通过实际的JAVA程序范例和UML图示来一一解说,书中有代码的重要部分加上标注使读者更加容易解读,再配合众多的说明图解,无论对于初学者还是程序设计高手来说,这都是一本学习和认识设计模式非常难得的好书。 最后附上练习问题,让读者可以温故而知新,能快速地吸收书中的精华,书中最后附上练习问题解答,方便读者学习验证。 立即下载

相关推荐

java多线程实现大批量数据导入源码

java多线程实现大批量数据切分成指定份数的数据,然后多线程处理入库或者导出,线程的个数和每份数据的数量都可以控制

【多线程】线程数量大于核心线程数,后续线程进来时,是会创建一个新的线程还是放到阻塞队列中?

线程数量大于核心线程数,后续线程进来时,是会创建一个新的线程还是放到阻塞队列中

weixin_45325628的博客 2984

Java多线程编程全部源码

这个资源是学习java多线程的全部代码,之前上传的是部分章节的,看我博客的时候,可以参照着代码看。

Java多线程初阶(二)(图片+源码+超详细)

文章包含多线程的状态,多线程安全问题以及解决多线程安全问题的做法,synchronized、volatile关键字的使用,wait和notify/notifyAll方法的解释

小S的博客 1004

java多线程源码,仅供参考

在学习java多线程的过程中,用这个例子可以很好的理解java多线程的运行原理,参考《java核心技术上》。

State of GPT (ChatGPT 原理及现状介绍)

要用 RLHF 的一个简单的原因就是 RLHF 模型效果更好,下面的图来源于 InstructGPT 论文,PPO (RLHF 算法) 后的模型生成的答案更被人类喜欢。karpathy 认为 RLHF 有用的原因是判别比生成更容易,让标注员去写一些 SFT 的 QA 数据对是比较难的,如果有一个 SFT 模型生成一些数据让标注员判断哪个更好就简单很多。,GPT4 最强,前三个都是 RLHF 模型,其他模型都是 SFT 模型。伯克利做的模型评测榜单。

kebijuelun的博客 3578

Java多线程(超级超级详细版)

Java 多线程编程是 Java 语言中的一个重要部分,它能够让我们在一个程序中同时执行多个任务。本文档将深入讲解 Java 多线程的核心概念、实现方式、同步机制以及实际应用案例。包含Thread线程方法,线程状态,线程安全,线程案例等内容。

featureA的博客 1720

Stata实战:5步搞定PSM倾向得分匹配(附完整代码与常见报错解决)

本文详细介绍了使用Stata进行PSM(倾向得分匹配)的5步实战流程,包括数据预处理、模型构建、匹配算法选择、匹配质量诊断和结果解释。提供完整代码示例和常见报错解决方案,帮助研究者高效完成实证分析,提升匹配成功率。

weixin_29214559的博客 590

【线程】Java多线程代码案例(1)

本文整理了多线程代码案例中的单例模式和阻塞队列,并充分考虑了线程安全问题。

telasi2004的博客 1647

FreeRTOS CLI实战:5分钟搞定GD32串口终端移植(附LED控制源码)

本文详细介绍了如何在GD32芯片上快速移植FreeRTOS CLI(Command Line Interface)终端,实现串口交互式调试。通过DMA+空闲中断配置和LED控制命令实战,开发者可在5分钟内构建高效嵌入式调试工具,显著提升现场维护效率。

weixin_28707365的博客 450

solidworks实体显示线框

solidworks实体莫名其妙显示为有点虚的线框

qq_35858902的博客 1万+

多线程基础必要知识点!看了学习多线程事半功倍(转)

多线程三分钟就可以入个门了! 原创:JavayJava3y4月17日 前言 之前花了一个星期回顾了Java集合: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码剖析】 LinkedHashMap就这么简单【源码剖析】 TreeMap就这么简单【...

weixin_34074740的博客 300

cartographer错误终结者 ordered_multi_queue.cc:155 Queue waiting for data

错误描述 错误提示的感应器数据可能是: Queue waiting for data:(0, points2) Queue waiting for data:(0, odom) 说明在提供给cartographter的传感器数据出现了问题,没有被成功解析。 原因分析 小编是使用自己编写的odom话题发布时,cartographer爆出了该错误。 于是设想传感器数据相关的tf或lua配置或数据本身存在错误。 于是逐个排查,最终发现了错误原因。 排查可能原因:tf错误 小编之前已处理过tf与lua配置的问题 参

人工智能机器人 4157

多线程基础知识点

多线程基础知识点 多线程基础必要知识点!看了学习多线程事半功倍

qq_41854398的博客 186

STM32驱动HC-SR04超声波模块

本篇文章将带大家使用HAL库驱动HC-SR04超声波模块。超声波模块作为智能小车必备的模块,要学习智能小车的同学是必须掌握好这个模块的使用的。HC-SR04是一种常用的超声波传感器模块,也被称为超声波测距模块,广泛应用于各种自动化控制和测距系统中。它通常由超声波发射器、接收器、控制电路和外壳组成,可以实现非接触式的距离测量。下面是HC-SR04超声波模块的一些特性和技术参数:距离测量范围:2cm ~ 400cm(可调)。测量精度:0.3cm。工作电压:DC 5V。工作电流:15mA。

m0_49476241的博客 7684

由于找不到mfc110u.dll,无法继续执行代码,重新安装程序可能会解决此问题的 解决方案

本文主要介绍了由于找不到mfc110.dll,无法继续执行代码的解决方案,希望能对使用Windows的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 5万+

2023年以就业为目的学习Java还有必要吗?

Java 作为一门在业界广泛应用的编程语言,只要能学会它就一定可以找到用武之地。零基础学习者也不必担心,耐心地跟着《Java编程动手学》一步一步学,就能从入门走向精通。▲点击即可购书总结一下本书的特点:· 内容翔实:涵盖了 Java 技术的精髓,保证学有所用;· 语言通俗易懂:小白也能看懂,简洁明白;· 实例丰富:数百个代码示例,覆盖所有知识点;· 图文并茂:精心绘制的图示,将复杂逻辑解释得很清楚。图片希望同学们都能学会 Java,找到心仪的工作,在职场上拥有美好的未来。—END—

HHX_01的博客 6747

华东理工大学计算机预推免面试

华东理工大学预推免面试经验分享

D1379951481的博客 1402

2023年以就业为目的学习Java还有必要吗?(文末送书)

文末送5本《Java编程动手学》

学Java,找哪吒 6704

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

从零基础到Java高手,这条学习路线助你事半功倍

最新接入DeepSeek-V3模型,点击下载最新版本InsCode AI IDE 标题:从零基础到Java高手,这条学习路线助你事半功倍 在当今数字化时代,掌握一门编程语言已经成为许多人职业发展的必备技能。而Java作为全球最受欢迎的编程语言之一,其应用范围广泛,无论是企业级开发、安卓应用还是大数据处理,Java都占据着举足轻重的地位。然而,对于初学者来说,Java的学习曲线并不平坦,尤其是在面对...

SapphireFox89的博客 322

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Java入门基础知识点需要学什么?

入门学习一定要找到适合自己的方法才能事半功倍,对需要掌握的知识点有一个大概的了解,Java入门基础知识包含:标识符、变量、AScii码和Unicod码、基本数据类型转化String类、进制、运算符、程序流程控制语句、方法等内容。 Java入门基础知识点需要掌握的: 一、静态方式和属性 静态方法和属性用于描述某一类对象群体的特征,而不是单个对象的特征。Java中大量应用了静态方法和属性。但这种技巧在很多语言中不被频繁地使用。理解静态方法和属性对于理解类与对象的关系是十分有帮助的,在大量的Ja..

deqing271的博客 135

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

CodeGym:以游戏化的方式学习Java真的是事半功倍

说起编程语言的话、很多人可能就要问,现在学哪种语言最吃香呢?当下哪一种语言最火呢?这个咱们先不着急回答,先看下https://www.tiobe.com/tiobe-index/给出的排名,明显看出在近几年一直排名前3位,语言用的人越多,也越收欢迎,也就意味着高收入的工作机会。 二十年前成为一名程序员要困难得多,当时只能照着“枯燥”的书来学习。如今,有几十种在线课程和平台供学生使用,可以随意编写代码。今天,让我们看一下这个网站,它是一个学习和实践Java的游戏化在线平台,适合零基础进行学习.

java李阳勇的博客 1万+
上一篇: Thread源码剖析
下一篇: Java锁机制了解一下
Java3y
Java3y 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 1万+粉丝 509原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值