Java并发编程之线程修炼

两年经验的java程序员可以看哪些实用进阶的书籍? 不废话,直接推荐几本我读过的、自认不错的书。 1、网络编程书籍 网络编程,虽然有了 Netty 框架等很优质的网络框架了,但是理解背后的原理,对咱们定位项目的 bug,优化代码的性能,帮助非常大。 《Java TCP/IP Socket编程》(豆瓣 8.0 分) 这本书,我们既可以去学到必要的网络基础,也能学会怎么用 Java 去网络编程,非常推荐。 对于网络编程,大家都知道,这是易学难精的技能。学懂原理,了解网络编程,看看我上面推荐的书就够了。但是,要精通网络编程,写好网络相关的程序,那是相当不 阅读详情


### 前言
Java 提供了多线程编程内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

- 多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

- 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。


一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。


### 线程的生命周期
线程的生命周期是此线程创建到此线程死亡的过程。如下图:

![threadsatus](https://blog-ituac-1252741530.cos.ap-beijing.myqcloud.com/threadstatus_1570872397334.png)

生命周期完整状态:
1. 初始化状态:线程创建,但是还没有调用start()方法。
2. 运行状态:就绪状态和运行中状态在这里合并为运行状态。
3. 阻塞状态:线程阻塞,线程等待进入synchronized修饰的代码块或方法(方法包含:sleep()、suspend()、wait()、join()等方法)。
4. 等待状态:处于要调用notify()或notifyAll()进行唤醒通知才可以运行。
5. 超时等待状态:线程进入等待状态,在指定时间后自行返回。
6. 终止状态:线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。


### 创建线程
创建线程有三种方式:
1. 通过实现 Runnable 接口
2. 通过继承 Thread 类本身
3. 通过 Callable 和 Future 创建线程


### 线程优先级
Java 线程优先级使用 1 ~ 10 的整数表示:
1. 最低优先级 1:Thread.MIN_PRIORITY
2. 最高优先级 10:Thread.MAX_PRIORITY
3. 普通优先级 5:Thread.NORM_PRIORITY 【父线程默认】

ps:父线程默认优先级是普通优先级5,子线程默认优先级是父优先级,也就是子线程默认优先级并非是普通优先级5,当父线程优先级修改后创建子线程,子线程则会是当前父线程的优先级。(好不通顺^0^,应该可以理解)


如下代码操作线程优先级:
```java
// 获取线程优先级
Thread.currentThread().getPriority()

//设置线程优先级
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
Thread.currentThread().setPriority(6);

```
Priority 设置范围在 1-10,不然会发生java.lang.IllegalArgumentException 异常。


### 线程间通信
子线程执行完毕通知主线程处理某些逻辑的场景还是比较容易遇见的业务场景,这就需要用到线程间的通信。

- 非线程安全(暂停、恢复、停止操作)

Thread提供的方法可以实现对线程进行暂停suspend(),恢复resume(),停止stop()的操作
```java
/**
 * 
 * @author ituac
 */

public class ThreadNotify implements Runnable{

    public void run() {
        while (true){
                try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName()+"输出helloword");
                }catch (InterruptedException e){
                System.err.println(e.getMessage());
              }
           }
    }
    
    public static void main(String[] args) throws Exception{
        Runnable runnable = new ThreadNotify();
        Thread thread = new Thread(runnable,"operationThread");
        /**
         * 启动,输出helloword
         */
        thread.start();
        TimeUnit.SECONDS.sleep(3L);

        /**
         * 线程暂停,不在输出helloword
         */
        System.out.println("线程暂停:"+LocalTime.now());
        thread.suspend();
        TimeUnit.SECONDS.sleep(3L);

        /**
         * 线程恢复,继续输出helloword
         */
        System.out.println("线程恢复:"+LocalTime.now());
        thread.resume();
        TimeUnit.SECONDS.sleep(3L);

        /**
         * 线程停止,不在输出helloword
         */
        thread.stop();
        System.out.println("线程停止:"+LocalTime.now());
        TimeUnit.SECONDS.sleep(3L);
    }
    
}

```

运行结果如下:

![cc1](https://blog-ituac-1252741530.cos.ap-beijing.myqcloud.com/cs11_1570874724137.png)


- 线程安全(等待/通知机制)

线程安全的暂停,恢复操作可以使用等待/通知机制代替,等待通知模式是 Java 中比较经典的线程通信方式。

相关API操作

|方法名|描述|
|-------|-------|
|notify()|通知一个在对象上等待的线程,使其重wait()方法中返回,前提是该线程获得了对象的锁|
|notifyAll()|通知所有等待在该对象上的线程|
|wait()|只有等待另外线程的通知或被中断才会返回,调用该方法会释放对象的锁|
|wait(long)|设置超时等待一段时间(毫秒),如果超过时间就返回|
|wait(long,int)|超时时间细粒度的控制,可以达到纳秒|


通过上述APi操作线程通信,如下代码:

```java
/**
 * @author ituac
 */
public class ThreadWaitNotify {
    
    private int startNum = 1;
    
    private boolean flag = false;
    
    //奇数计算
    public static class ThreadA implements Runnable{

        private ThreadWaitNotify threada;
        
        public ThreadA(ThreadWaitNotify threada) {
            this.threada = threada;
        }
        
        public void run() {
            while (threada.startNum <= 50) {
                synchronized (ThreadWaitNotify.class) {
                    //System.out.println("奇数线程抢到锁了");
                    if (!threada.flag) {
                        System.out.println(Thread.currentThread().getName() + "+-+奇数" + threada.startNum);
                        threada.startNum++;
                        threada.flag = true;
                        ThreadWaitNotify.class.notify();
                    }else {
                        try {
                            ThreadWaitNotify.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }    
                }
            }
        }    
    }
    
    
    //偶数计算
    public static class ThreadB implements Runnable{

        private ThreadWaitNotify threadb;
        
        public ThreadB(ThreadWaitNotify threadb) {
            this.threadb = threadb;
        }
        
        public void run() {
            while (threadb.startNum <= 50) {
                synchronized (ThreadWaitNotify.class) {
                    // System.out.println("偶数线程抢到锁了");
                    if (threadb.flag) {
                        System.out.println(Thread.currentThread().getName() + "+-+偶数" + threadb.startNum);
                        threadb.startNum++;
                        threadb.flag = false;
                        ThreadWaitNotify.class.notify();
                    }else {
                        try {
                            ThreadWaitNotify.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }    
                }
            }
        }    
    }
    
    
    public static void main(String[] args) {
        ThreadWaitNotify thread = new ThreadWaitNotify();
        Thread t1 = new Thread(new ThreadA(thread));
        t1.setName("A");
        Thread t2 = new Thread(new ThreadB(thread));
        t2.setName("B");
        t1.start();
        t2.start();
        
        
    }

}

```

运行结果如下:

![xcaq](https://blog-ituac-1252741530.cos.ap-beijing.myqcloud.com/xcaq_1570876159797.png)

安全线程操作中需要注意的是:
1. wait() 、nofify() 、nofityAll() 调用的前提都是获得了对象的锁(也可称为对象监视器)。
2. 调用 wait() 方法后线程会释放锁,进入 WAITING 状态,该线程也会被移动到等待队列中。
3. 调用 notify() 方法会将等待队列中的线程移动到同步队列中,线程状态也会更新为 BLOCKED
4. 从 wait() 方法返回的前提是调用 notify() 方法的线程释放锁,wait() 方法的线程获得锁。


等待通知经典范式: 
1. 线程 A 作为消费者
2. 获取对象的锁
3. 进入 while(判断条件),并调用 wait() 方法。
4. 当条件满足跳出循环执行具体处理逻辑
5. 线程 B 作为生产者
6. 获取对象锁。
7. 更改与线程 A 共用的判断条件
8. 调用 notify() 方法

上述范式伪代码:
```java

//Thread A
synchronized(Object){
    while(条件){
        Object.wait();
    }
    //do something
}
//Thread B
synchronized(Object){
    条件=false;//改变条件
    Object.notify();
}
```


### Thread.join()

Thread.join()作用是等待该线程终止。

相关API操作

|方法名|作用|
|-------|-------|
|join()|A线程调用B线程的join()方法后,那么A线程需要等待B线程终止,才可以继续执行|
|join(long)|在join()方法的基础上增加了时间限制(毫秒),超出时间后,无论A线程是否执行完,当前线程都进入就绪状态,重新等待cpu调用|
|join(long,int)|超时时间细粒度的控制,可以达到纳秒|

```java
       private static void join() throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("t1");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }) ;
        
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                System.out.println("t2");
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }) ;
        t1.start();
        t2.start();
        //等待线程1终止
        t1.join();
        //等待线程2终止
        t2.join();
        System.out.println("thread end");
    }

```

ps: join()方法实现等待其实是调用了wait()方法,isAlive()方法的作用是监测子线程是否终止,如果终止或者超过了指定时间,代码就继续往下执行,否则就继续等待,直到条件成立。

### volatile 共享内存
Java 是采用共享内存的方式进行线程通信的,所以可以采用以下方式用主线程关闭 A 线程:

```java
public class Volatile implements Runnable{
    private static volatile boolean flag = true ;
    @Override
    public void run() {
        while (flag){
            System.out.println(Thread.currentThread().getName() + "运行中");
        }
        System.out.println(Thread.currentThread().getName() +"执行完成");
    }
    public static void main(String[] args) throws InterruptedException {
        Volatile aVolatile = new Volatile();
        new Thread(aVolatile,"thread A").start();
        System.out.println("main 线程正在运行") ;
        TimeUnit.MILLISECONDS.sleep(100) ;
        aVolatile.stopThread();
    }
    private void stopThread(){
        flag = false ;
    }
}
```
volatile定义的flag 存放于主内存中,所以主线程和线程 A 都可以看到。
volatile怎么实现存放在内存,可了解volatile原理(自行百度谢谢^-^)。

### ThreadLocal

ThreadLocal,叫线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构,ThreadLocal类型的变量在每个线程中是独立的,在多线程环境下不会相互影响

```java
    private static String STATE;
    private static ThreadLocal<String> STRING_THREAD_LOCAL = new InheritableThreadLocal<>();


    public static void main(String[] args) throws InterruptedException{

        STATE = "未重置";
        STRING_THREAD_LOCAL.set("未重置");

        Thread thread = new Thread(() ->
        {
            STATE = "已重置";
            STRING_THREAD_LOCAL.set("已重置");
            System.out.println(Thread.currentThread().getName() + " : 变量已重置");
        });
        thread.start();
        thread.join();

        System.out.println(Thread.currentThread().getName() + "STATE : " + STATE);
        System.out.println(Thread.currentThread().getName() + "STRING_THREAD_LOCAL : " + STRING_THREAD_LOCAL.get());
    }

```

### CountDownLatch 并发工具

CountDownLatch 可以实现 join 相同的功能,相对而言更加方便,实例如下:
```java
private static void countDownLatch() throws Exception{
    int thread = 3 ;
    long start = System.currentTimeMillis();
    final CountDownLatch countDown = new CountDownLatch(thread);
    for (int i= 0 ;i<thread ; i++){
        new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info("线程开始");
                try {
                    Thread.sleep(100);
                    countDown.countDown();
                    LOGGER.info("线程结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    countDown.await();
}
```

CountDownLatch 基于 AQS(AbstractQueuedSynchronizer) 实现。

CountDownLatch思路如下:
1. 初始化一个 CountDownLatch 时告诉并发的线程,然后在每个线程处理完毕之后调用 countDown() 方法。
2. 此方法会将 AQS 内置的一个 state 状态 -1 。
3. 最终在主线程调用 await() 方法,它会阻塞直到 state == 0 的时候返回。

Ps:CyclicBarrier工具,叫做屏障,也可以用于线程间通信,它可以等待 N 个线程都达到某个状态后继续运行的效果。之后再做展示。

### 管道通信

Java 虽说是基于内存通信的,也可以使用管道进行通信,管道通信概念对于netty开发者并不陌生,代码如下:
```java
/**
 * @author ituac
 */
public class ThreadPip {

    
    public static void piped() throws IOException {
        //面向于字符 PipedInputStream 面向于字节
        final PipedWriter writer = new PipedWriter();
        final PipedReader reader = new PipedReader();
        //输入输出流建立连接
        writer.connect(reader);
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("线程1已运行");
                try {
                    for (int i = 0; i < 10; i++) {
                        System.out.println("线程1发送线程2第"+i+"条消息");
                        writer.write(i+"");
                        Thread.sleep(10);
                    }
                } catch (Exception e) {
                } finally {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                System.out.println("线程2已运行");
                int msg = 0;
                try {
                    while ((msg = reader.read()) != -1) {
                        System.out.println("msg="+(char) msg);
                    }
                } catch (Exception e) {
                }
            }
        });
        t1.start();
        t2.start();
    }
    
    public static void main(String[] args) throws IOException {
        piped();
    }
    
}
```
运行结果如下:
![theadpip](https://blog-ituac-1252741530.cos.ap-beijing.myqcloud.com/thread1111_1570878328370.png)

### 小结

在实际应用开发中可以根据需求选择最业务场景所适合的线程通信方式。
即将发布下一篇章Java并发编程之线程池修炼。

本文由 波罗星 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://blog.ituac.com/archives/thread


 

林浩然的Java修炼记:从新手到高手的奇趣阅读之旅 在一个阳光明媚的午后,我们的主角林浩然,一个热衷于编程的Java小哥,正在书桌前挠头思索。他坚信“学习的最好途径就是看书”,并且深谙这个道理,因此他的书房里堆满了各类编程宝典。 阅读详情

相关推荐

Java工程师修炼之道 - 全面掌握Java知识与架构技术

在日常开发中,标准异常类往往不能完全满足特定业务需求,此时自定义异常就显得非常必要。创建自定义异常类非常简单,只需继承Exception类(对于检查型异常)或类(对于非检查型异常)。自定义异常的场景通常包括:当业务逻辑中出现特定的错误条件时。当标准异常类无法准确描述你的异常情况时。当需要对异常进行特定的处理时。多线程编程是现代软件开发中的一项重要技术。通过多线程,开发者可以充分利用多核处理器的能力,提高程序的性能和响应速度。

weixin_36444661的博客 709

一文读懂容错机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.youkuaiyun.com/u012482647/article/details/78148447 随着分布式、微服务项目的快速发展,各个服务之间的通讯,难免出现依赖关系,若某一个单元出现故障,就很容易因依赖关系而发生故障的蔓延,为了解决这样的问题,容错作为其中一项很重要的技术也广为人知。 导语 容错机制广义的理解,就是包含...

波罗星的博客 9157

删除结点

对给定的单链表 L ,设计一个算法,删除 L 中值为 x 的结点的直接前驱结点。 1 由键盘输入值,换行表示输入结束,根据输入建立单链表。 2 输入链表中的一个元素值,从链表中删除这个元素的前驱结点。 3 对删除元素后的链表元素在屏幕上显示。 测试用例1 11 22 33 44 55 66 77↵ 44↵ 期待输出1 11 22 44 55 66 77↵ #include <iostre...

SChenlyx 1618

分布式文件系统(GFS和HDFS)概述

前言 背景意义 分布式存储相关概念 分布式存储系统的分类 CAP理论 复制副本 一致性 GFS架构 租约(lease)和变更顺序 容错机制 前言 因为我有一门分布式的课,老师要求我们选择一个课题做汇报,有GFS、Hadoop、Bigtable、MapReduce、C...

Tyler_Zx的博客 1万+

嵌入式开发必看:STM32 DMA双缓冲配置全攻略(含CubeMX教程)

本文详细解析了STM32 DMA双缓冲机制的原理与优势,并提供了基于CubeMX的完整配置教程。通过硬件自动切换两个缓冲区,实现DMA传输与CPU处理的真正并行,有效降低中断频率,提升高速数据采集、音频流处理等场景下的系统效率和实时性。

weixin_29212429的博客 173

STM32F4XX软件模拟I2C实现MPU6050姿态数据采集

本文详细介绍了在STM32F4XX平台上通过软件模拟I2C驱动MPU6050传感器实现姿态数据采集的完整方案。内容涵盖软件I2C的GPIO配置、核心通信函数实现、MPU6050寄存器配置详解以及数据读取处理流程,为硬件资源受限的嵌入式应用提供了灵活的解决方案,并分享了调试技巧与常见问题处理方法。

butter的博客 678

容错机制的基本原理解析

1.背景介绍 容错机制是计算机科学和信息处理领域中的一个重要概念,它旨在确保系统在出现故障或错误时能够继续运行,并在可能的情况下恢复到正常状态。容错机制在计算机系统、网络通信、数据库管理等各个领域都有广泛的应用。本文将从以下几个方面进行深入解析: 背景介绍 核心概念与联系 核心算法原理和具体操作步骤以及数学模型公式详细讲解 具体代码实例和详细解释说明 未来发展趋势与挑战 附录常见问题与解答 ...

AI天才研究院 3881

2025年12月11日 Coherent收发器下沉到数据中心:调研

技术可行性已基本具备。随着3nm DSP技术的成熟和Coherent-Lite等低功耗方案的推出,Coherent技术与IMDD的功耗差距在800G及以上速率节点快速缩小。特别是在1.6T及更高速率,两种技术的功耗开始趋于收敛。同时,通过系统级优化,如将DSP功能从可插拔模块转移到ASIC芯片,可实现30%以上的功耗降低。在成本方面,硅光技术的进步使得单通道硅光引擎BOM成本降至18美元,较2020年下降62%,逼近InP方案的经济临界点。光学系统机会丰富多样。

weixin_51184047的博客 1168

容错机制与数据一致性

1.背景介绍 在当今的大数据时代,数据的可靠性和一致性成为了非常重要的问题。容错机制和数据一致性是解决这些问题的关键技术。在分布式系统中,数据可能在多个节点上存储和处理,因此需要一种机制来确保数据的一致性和可靠性。 容错机制是指在计算机系统中,为了确保系统的正常运行,采取的措施。容错机制可以防止系统出现故障,或者在故障发生时,尽快恢复系统的正常运行。容错机制包括硬件容错、软件容错和分布式容错等...

AI天才研究院 789

9 硬件工程师笔面试高频知识考点真题解析——运算放大器

运算放大器(运放)摘要 运放是一种高增益、差分输入的模拟集成电路,具有虚短(线性区输入电压相等)和虚断(输入电流为零)特性,支持负反馈(线性放大)和开环/正反馈(饱和区比较)两种工作模式。关键特性包括高输入阻抗、低输出阻抗,理想运放参数(如无限增益)与实际参数(如有限带宽)的差异。 选型要点: 信号特性:直流/低频信号关注失调电压(Vos)、温漂;高频信号侧重带宽(GBW)和压摆率(SR)。 电源条件:单/双电源供电、电压范围及功耗需求(如电池设备需低静态电流)。 精度与噪声:微弱信号需低噪声运放,高精度场

XU157303764的博客 1675

SCADA|KingSCADA通过组合框选择修改变量的值

《SCADA项目中组合框控件的使用方法》 本文详细介绍了SCADA系统中组合框(下拉框)控件的应用方法。通过具体示例演示如何实现:当选择"雷工笔记"时变量值为100,选择"雷工杂谈"时为200,选择"小李食代"时为300的功能。主要实现步骤包括:1)创建项目并添加组合框控件;2)设置Items选项及对应数值;3)创建内存变量;4)使用GetCurrentIndex()和GetItemData()方法;5)在选项变化事件中编写脚本。该控件在SCADA

做有价值的事,积累有价值的笔记! 864

精心修炼Java并发编程(JUC)-线程三大特性

此处的变量(Variables)与 Java 编程中所说的变量有所区别,它包括了实例字段、静态字段和构成数值对象的元素,但不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不会存在竞争问题。JMM 规定了。

qq_46248151的博客 1426

Python 爬虫实战:全球渔业船舶捕捞记录数据采集与分析全攻略

通过本项目,我们实现了对全球渔业船舶捕捞记录数据的采集、处理和可视化,为渔业资源管理和海洋生态保护提供了数据支持。未来,可以进一步结合机器学习和深度学习技术,对捕捞行为进行预测和分类,提高对非法捕捞活动的识别能力。

2201_76125261的博客 557

精心修炼Java并发编程(JUC)-线程池深度剖析

这张图展现了一个Java线程所有的状态。

qq_46248151的博客 1138

结构化数据特征构建神器openfe的实战代码

最近订阅专栏的几位同学一起参加了AI比赛,该比赛的数据形式是结构化的。随着特征和模型的不断迭代优化,达到了一个难以跨越的瓶颈。希望我能够给出一些有效的方法,帮助他们提高成绩。在个人的调研和体验后,向他们推荐了结构化数据特征构建神器openfe,最终也帮助他们提升了十来名的成绩,个人也感到十分的欣慰。特意写此博客,希望能够帮助更多的同学。 文章目录 1. 安装 2. 实战例子一 3. 实战例子二

herosunly的博客 3万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

不能错过的关于并发的好书

就像武林中的高手可以分身多重,同时应对多个敌人一样,Java并发编程允许程序同时执行多个任务,就像在多个地方同时出现一样。公司给小鸣和小佐各自安排了一项项任务,小佐凭借自己熟练的技艺,迅速的开始了,小鸣却还在慢悠悠的启动。相信看到这里,大家就知道是什么原因了,虽然小佐经验丰富、天赋异禀,但是小鸣深谙并发之道,多重影分身互相协调工作,效率突飞猛进。学成之后,将能了解到锁的奥秘,就像是锁定了分身的关键,保护了你的多线程程序免受混乱的干扰。快去探索吧,开启你的并发编程修炼之路,掌握多线程的艺术。

warmchuang的博客 82

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

精心修炼Java并发编程(JUC)-线程协同的一些基本操作(wait、notify...)

wait()notify()notify()这三个方法都用在我们的synchronized关键字中,对于锁对象,我们就可以操作他们的这三个方法结果分析:wait让出锁,t3得到执行,t3唤醒后,虽然t1先start,但是优先级低,所以t2优先执行(注意是概率,不是绝对的!

qq_46248151的博客 482

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

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

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Java7新特性(四)并发 3 CountDownLatch计数器对象

本文主要根据《Java程序员修炼之道》整理的代码笔记片段 示例代码(用于一半线程初始化后,才执行后续代码) public class ProcessingThread extends Thread { private final String ident; private final CountDownLatch latch; private static int MAX_THREADS

赤山岛 2451

2024年前端最全9本Java程序员必读的书_java必读书(1),2024年最新前端web面试

题外话,毕竟我工作多年,深知技术改革和创新的方向,Flutter作为跨平台开发技术、Flutter以其美观、快速、高效、开放等优势迅速俘获人心。

erhtre的博客 954

最早的90后Java开发已经31岁,最早的00后已进网易大厂,你在哪里?_30岁才进网易

我知道大家现在很忙,忙着工作,忙于家庭,忙于应酬……但是,这些难道比给自己投资更值得花时间吗?可以给工作花时间,给领导花时间,甚至是把时间花在玩乐上,但是就是不肯给自己的学习一点点时间?哪怕每天只学习一个多小时,长时间积累下来也会有不小的收获。不积跬步,无以至千里;不积小流,无以成江海。给自己一点点充电成长的时间,事业才能续航更久,走的更远。这是一笔很划算的买卖,何乐而不为?阿里伤透我心,疯狂复习刷题,终于喜提offer 哈哈~好啦,不闲扯了1、JAVA面试核心知识整理(PDF):包含JVM。

2401_84007015的博客 755

Java 学习修炼之路

Java 学习修炼之路

daweias的博客 454

java高级程序员修炼_Java程序员修炼之并发教程(一)

Java程序员修炼之并发教程(一)作者:小爱尚计算机的使用者一直以为他们的计算机可以同时做很多事情。他们认为当其他的应用程序在下载文件,管理打印队列或者缓冲音频的时候他们可以继续在文字处理程序上工作。甚至对于单个应用程序,他们任然期待它能在在同一时间做很多事情。举个例子,一个流媒体播放程序必须能同时完成以下工作:从网络上读取数字音频,解压缩数字音频,管理播放和更新程序显示。甚至文字处理器也应该能在...

weixin_42511557的博客 199

java修炼之多线程学习心得

java修炼之多线程学习心得 java是少数几种支持“多线程”的开发语言之一。大多数的程序语言只能遵循运行单独的一个程序块,无法同时运行多个不同的程序块。java的“多线程”恰可以弥补这个缺陷,它可以让同一进程中的不同线程一起运行起来,而不再像别的成语言只能进行单一的线程运行。如此一来就可以让程序运行起来更加的顺畅了,同时也可以达到多个任务同时被处理,更加充分利用计算机的CPU资源。 1. 进程与...

qq_41910568的博客 630

【并发编程视频资源分享】并发编程、多线程完整版学习视频分享

如果你是初学者,或者是自学者!你可以加小编微信(2782278837)!小编可以给你一些好的建议以及给你(免费)提供学习资料!你在学习上有什么问题都可以咨询小编!小编都会为你解答! 注:...

hello world! 703

线程大法修炼(多线程详解)

加瓦是一个普通的小青年 一日上山采药不小心跌落山崖,进一山洞,山洞有一秘籍名曰 **“线程大法”**,加瓦翻开书籍: 只见第一页赫然入眼曰: >**线程大法** 其旁有文字题注: 练此功分为五大阶段状态 ==1.新建状态 2.就绪状态 3.运行状态 4.阻塞状态 5.死亡状态== 加瓦看了看有点不可思议,“什么玩意,还死亡状态,我还想活呢!” 加瓦撇撇嘴不屑的就想离开。 谁知山洞剧烈摇晃,洞门封闭

weixin_63834061的博客 219
上一篇: TIDB小白安装手册
下一篇: 边缘计算的架构、赋能泛在应用
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值