想到多线程并发就心虚?先来巩固下这些线程基础知识吧!

只会CRUD的我,是如何横扫一切多线程问题,吊打面试官的呢? 写在前面 提起多线程编程,恐怕许多开发人员都会摇头表示不懂。确实,在校生和刚就职的开发人员往往很少有机会能够实现多线程编程。多数情况下,他们都是在开发框架下编写单线程的业务代码,而多线程的部分则被封装在了框架内部。即使是经验丰富的开发人员也会感叹他们曾经在多线程上栽过的跟头。但不可否认的是,多线程的确是一把利器 ,活用多线程有助于提高程序的响应性和吞吐量。可以毫不夸张地说,多线程是开发人员在继续“升级”的过程中必须打倒的一只“怪物”。 “设计模式”一词也常常会让开发人员感到畏惧。其实设计模式不过是对代码 阅读详情

前言和大纲

计算机系统里每个进程(Process)都代表着一个运行着的程序,进程是对运行时程序的封装,系统进行资源调度和分配的基本单位。

一个进程下可以有很多个线程,线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发,线程同时也是操作系统可识别的最小执行和调度单位。

在 Java 里线程是程序执行的载体,我们写的代码就是由线程运行的。有的时候为了增加程序的执行效率,我们不得不使用多线程进行编程,虽然多线程能最大化程序利用 CPU 的效率,但也是程序事故多发、程序员脱发的最大诱因。主要是平时我们的思维默认是单线程的,写多线程的时候得可以切换一下才行,这就要求我们对线程的基础知识了解的比较透彻。

这篇文章咱们总结一下 Java线程的基础,多掌握点,以后就少掉点头发,不光省下植发的钱,工资还能往上涨,这么一想简直双赢。本文的大纲如下:

好了让我们开始今天的正文,直接上代码吧。

Java 中的线程

到目前为止,我们写的所有 Java 程序代码都是在由JVM给创建的 Main Thread 中单线程里执行的。Java 线程就像一个虚拟 CPU,可以在运行的 Java 应用程序中执行 Java 代码。当一个 Java 应用程序启动时,它的入口方法 main() 方法由主线程执行。主线程(Main Thread)是一个由 Java 虚拟机创建的运行你的应用程序的特殊线程。

因为 Java 里一切皆对象,所以线程也是用对象表示的,线程是类 java.lang.Thread 类或者其子类的实例。在 Java 应用程序内部, 我们可以通过 Thread 实例创建和启动更多线程,这些线程可以与主线程并行执行应用程序的代码。

创建和启动线程

在 Java 中创建一个线程,就是创建一个 Thread 类的实例

  Thread thread = new Thread();
复制代码

启动线程就是调用线程对象的 start() 方法

  thread.start();
复制代码

当然,这个例子没有指定线程要执行的代码,所以线程将在启动后立即停止。

指定线程要执行的代码

有两种方法可以给线程指定要执行的代码。

  • 第一种是,创建 Thread 的子类,覆盖父类的 run() 方法,在run() 方法中指定线程要执行的代码。
  • 第二种是,将实现 Runnable (java.lang.Runnable) 的对象传递给 Thread 构造方法,创建Thread 实例。

其实,还有第三种用法,不过细究下来可归类到第二种的特殊使用方式,下面我们看看这三种的用法和区别。

通过 Thread 子类指定要执行的代码

通过继承 Thread 类创建线程的步骤:

  1. 定义 Thread 类的子类,并覆盖该类的 run() 方法。run() 方法的方法体就代表了线程要完成的任务,因此把 run 方法称为执行体。
  2. 创建 Thread 子类的实例,即创建了线程对象。
  3. 调用线程对象的 start 方法来启动该线程。
package com.learnthread;

public class ThreadDemo {

    public static void main(String[] args) {
        // 实例化线程对象
        MyThread threadA = new MyThread("Thread 线程-A");
        MyThread threadB = new MyThread("Thread 线程-B");
        // 启动线程
        threadA.start();
        threadB.start();
    }

    static class MyThread extends Thread {

        private int ticket = 5;

        MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }
        }

    }

}
复制代码

上面的程序,主线程启动调用A、B两个线程的 start() 后,并没有通过wait() 等待他们执行结束。A、B两个线程的执行体,会并发地被系统执行,等线程都直接结束后,程序才会退出。

通过实现 Runnable 接口指定要执行的代码

Runnable 接口里,只有一个 run() 方法的定义:

package java.lang;
public interface Runnable {
    public abstract void run();
}
复制代码

其实,Thread 类实现的也是 Runnable 接口。 在 Thread 类的重载构造方法里,支持接收一个实现了 Runnale 接口的对象作为其 target 参数来初始化线程对象。

public class Thread implements Runnable {

    ...
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    ...
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
    ...

}

复制代码

通过实现 Runnable 接口创建线程的步骤:

  1. 定义 Runnable 接口的实现,实现该接口的 run 方法。该 run 方法的方法体同样是线程的执行体。
  2. 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
  3. 调用线程对象的 start 方法来启动线程并执行。
package com.learnthread;

public class RunnableDemo {

    public static void main(String[] args) {
        // 实例化线程对象
        Thread threadA = new Thread(new MyThread(), "Runnable 线程-A");
        Thread threadB = new Thread(new MyThread(), "Runnable 线程-B");
        // 启动线程
        threadA.start();
        threadB.start();
    }

    static class MyThread implements Runnable {

        private int ticket = 5;

        @Override
        public void run() {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }
        }

    }

}
复制代码

运行上面例程会有以下输出,同样程序会在所以线程执行完后退出。


Runnable 线程-B 卖出了第 5 张票
Runnable 线程-B 卖出了第 4 张票
Runnable 线程-B 卖出了第 3 张票
Runnable 线程-B 卖出了第 2 张票
Runnable 线程-B 卖出了第 1 张票
Runnable 线程-A 卖出了第 5 张票
Runnable 线程-A 卖出了第 4 张票
Runnable 线程-A 卖出了第 3 张票
Runnable 线程-A 卖出了第 2 张票
Runnable 线程-A 卖出了第 1 张票

Process finished with exit code 0
复制代码

既然是给 Thread 传递 Runnable 接口的实现对象即可,那么除了普通的定义类实现接口的方式,我们还可以使用匿名类和 Lambda 表达式的方式来定义 Runnable 的实现。

  • 使用 Runnable 的匿名类作为参数创建 Thread 对象:
Thread threadA = new Thread(new Runnable() {
    private int ticket = 5;
    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
            ticket--;
        }
    }
}, "Runnable 线程-A");
复制代码
  • 使用实现了 Runnable 的 Lambda 表达式作为参数创建 Thread 对象:
Runnable runnable = () -> { System.out.println("Lambda Runnable running"); };
Thread threadB = new Thread(runnable, "Runnable 线程-B");

复制代码

因为,Lambda 是无状态的,定义不了内部属性,这里就举个简单的打印一行输出的例子了,理解一下这种用法即可。

获取线程的执行结果

上面两种方法虽然能指定线程执行体里要执行的任务,但是都没有返回值,如果想让线程的执行体方法有返回值,且能被外部创建它的父线程获取到返回值,就需要结合J.U.C 里提供的 Callable、Future 接口来实现线程的执行体方法才行。

J.U.C 是 java.util.concurrent 包的缩写,提供了很多并发编程的工具类,后面会详细学习。

Callable 接口只声明了一个方法,这个方法叫做 call():

package java.util.concurrent;

public interface Callable<V> {
    V call() throws Exception;
}
复制代码

Future 就是对于具体的 Callable 任务的执行进行取消、查询是否完成、获取执行结果的。可以通过 get 方法获取 Callable 的 call 方法的执行结果,但是要注意该方法会阻塞直到任务返回结果。

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
复制代码

Java 的 J.U.C 里给出了 Future 接口的一个实现 FutureTask,它同时实现了 Future 和 Runnable 接口,所以,FutureTask 既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值。

下面是一个 Callable 实现类和 FutureTask 结合使用让主线程获取子线程执行结果的一个简单的示例:

package com.learnthread;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableDemo implements Callable<Integer> {
    @Override
    public Integer call() {
        int i = 0;
        for (i = 0; i < 20; i++) {
            if (i == 5) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return i;
    }

    public static void main(String[] args) {
        CallableDemo tt = new CallableDemo();
        FutureTask<Integer> ft = new FutureTask<>(tt);
        Thread t = new Thread(ft);
        t.start();
        try {
            System.out.println(Thread.currentThread().getName() + " " + ft.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
复制代码

上面我们把 FutureTask 作为 Thread 构造方法的 Runnable 类型参数 target 的实参,在它的基础上创建线程, 执行逻辑。所以本质上 Callable + FutureTask 这种方式也是第二种通过实现 Runnable 接口给线程指定执行体的,只不过是由 FutureTask 包装了一层,由它的 run 方法再去调用 Callable 的 call 方法。例程运行后的输出如下:

Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
main 5
复制代码

Callable 更常用的方式是结合线程池来使用,在线程池接口 ExecutorService 中定义了多个可接收 Callable 作为线程执行任务的方法 submit、invokeAny、invokeAll 等,这个等学到线程池了我们再去学习。

初学者常见陷阱--在主线程里调用 run()

在刚开始接触和学习 Java 线程相关的知识时,一个常见的错误是,在创建线程的线程里,调用 Thread 对象的 run() 方法而不是调用 start() 方法。

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Anonymous Runnable running");
    }
};
Thread newThread = new Thread(myRunnable);
newThread.run();  // 应该调用 newThread.start();
复制代码

起初你可能没有注意到这么干有啥错,因为 Runnable 的 run() 方法正常地被执行,输出了我们想要的结果。

但是,这么做 run() 不会由我们刚刚创建的新线程执行,而是由创建 newThread 对象的线程执行的 。要让新创建的线程--newThread 调用 myRunnable 实例的 run() 方法,必须调用 newThread.start() 方法才行。

线程对象的基本用法

线程对象常用的方法

Thread 线程常用的方法有以下这些:

方法描述
run线程的执行实体,不需要我们主动调用,调用线程的start() 就会执行run() 方法里的执行体
start线程的启动方法。
Thread.currentThreadThread 类提供的静态方法,返回对当前正在执行的线程对象的引用。
setName设置线程名称。
getName获取线程名称。
setPriority设置线程优先级。Java 中的线程优先级的范围是 [1,10],一般来说,高优先级的线程在运行时会具有优先权。可以通过 thread.setPriority(Thread.MAX_PRIORITY) 的方式设置,默认优先级为 5。
getPriority获取线程优先级。
setDaemon设置线程为守护线程。
isDaemon判断线程是否为守护线程。
isAlive判断线程是否启动。
interrupt中断线程的运行。
Thread.interrupted测试当前线程是否已被中断。
join可以使一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。
Thread.sleep静态方法。将当前正在执行的线程休眠。
Thread.yield静态方法。将当前正在执行的线程暂停,让出CPU,让其他线程执行。

线程休眠

使用 Thread.sleep 方法可以使得当前正在执行的线程进入休眠状态。 使用 Thread.sleep 需要向其传入一个整数值,这个值表示线程将要休眠的毫秒数。 Thread.sleep 方法可能会抛出 InterruptedException,因为异常不能跨线程传播回主线程中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。

public class ThreadSleepDemo {

    public static void main(String[] args) {
        new Thread(new MyThread("线程A", 500)).start();
        new Thread(new MyThread("线程B", 1000)).start();
        new Thread(new MyThread("线程C", 1500)).start();
    }

    static class MyThread implements Runnable {

        /** 线程名称 */
        private String name;

        /** 休眠时间 */
        private int time;

        private MyThread(String name, int time) {
            this.name = name;
            this.time = time;
        }

        @Override
        public void run() {
            try {
                // 休眠指定的时间
                Thread.sleep(this.time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.name + "休眠" + this.time + "毫秒。");
        }

    }

}
复制代码

上面例程开启了3个线程,在各自的线程执行体里让各自线程休眠了 500、1000 和 1500 ms ,线程 C 休眠结束后,整个程序退出。

线程A休眠500毫秒。
线程B休眠1000毫秒。
线程C休眠1500毫秒。

Process finished with exit code 0
复制代码

终止线程

当一个线程运行时,另一个线程可以直接通过 interrupt 方法中断其运行状态。

public class ThreadInterruptDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 实例化Runnable实现类的对象
        Thread t = new Thread(mt, "线程"); // 实例化Thread对象
        t.start(); // 启动线程
        try {
            Thread.sleep(2000); // 主线程休眠2秒
        } catch (InterruptedException e) {
            System.out.println("主线程休眠被终止");
        }
        t.interrupt(); // 中断 mt 线程的执行
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println("1、进入run()方法");
            try {
                Thread.sleep(10000); // 线程休眠10秒
                System.out.println("2、已经完成了休眠");
            } catch (InterruptedException e) {
                System.out.println("3、MyThread线程休眠被终止");
                return; // 返回调用处
            }
            System.out.println("4、run()方法正常结束");
        }
    }
}
复制代码

如果一个线程的 run 方法执行一个无限循环,并且没有执行 sleep 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt 方法就无法使线程提前结束。

不过调用 interrupt 方法会设置线程的中断标记,此时被设置中断标记的线程再调用 interrupted 方法会返回 true。因此可以在线程的执行体循环体中使用 interrupted 方法来判断当前线程是否处于中断状态,从而提前结束线程。

看下面这个,可以有效终止线程执行的示例

package com.learnthread;

import java.util.concurrent.TimeUnit;

public class ThreadInterruptEffectivelyDemo {
    public static void main(String[] args) throws Exception {
        MyTask task = new MyTask();
        Thread thread = new Thread(task, "线程-A");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(50);
        thread.interrupt();
    }

    private static class MyTask implements Runnable {

        private volatile long count = 0L;

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 线程启动");
            // 通过 Thread.interrupted 和 interrupt 配合来控制线程终止
            while (!Thread.interrupted()) {
                System.out.println(count++);
            }
            System.out.println(Thread.currentThread().getName() + " 线程终止");
        }
    }
}
复制代码

主线程在启动线程-A后,主动休眠50毫秒,线程-A的执行体里会不断打印计数器的值,等休眠结束后主线程通过调用线程-A的 interrupt 方法设置了线程的中断标记,这时线程-A的执行体中通过 Thread.interrupted() 就能判断出线程被设置了中断状态,随后结束执行退出。

守护线程

什么是守护线程?

  • 守护线程(Daemon Thread)是在后台执行并且不会阻止 JVM 终止的线程。当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。
  • 与守护线程(Daemon Thread)相反的,叫用户线程(User Thread),也就是非守护线程。

为什么需要守护线程?

  • 守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。典型的应用就是垃圾回收器。

如何使用守护线程?

  • 可以使用 isDaemon 方法判断线程是否为守护线程。
  • 可以使用 setDaemon 方法设置线程为守护线程。
    • 正在运行的用户线程无法设置为守护线程,所以 setDaemon 必须在 thread.start 方法之前设置,否则会抛出 llegalThreadStateException 异常;
    • 一个守护线程创建的子线程依然是守护线程。
    • 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑就不能。
public class ThreadDaemonDemo {

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread(), "线程");
        t.setDaemon(true); // 此线程在后台运行
        System.out.println("线程 t 是否是守护进程:" + t.isDaemon());
        t.start(); // 启动线程
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "在运行。");
            }
        }
    }
}
复制代码

Java 线程的生命周期

java.lang.Thread.State 中定义了 6 种不同的线程状态,在给定的一个时刻,线程只能处于其中的一个状态。 下图给出了这六种状态,注意中间的 Ready 和 Running 都属于 Runnable 就绪状态。

以下是各状态的说明,以及状态间的联系:

  • 新建(New) - 尚未调用 start 方法的线程处于此状态。此状态意味着:线程创建了但尚未启动。

  • 就绪(Runnable) - 已经调用了 start 方法的线程处于此状态。此状态意味着:线程已经在 JVM 中运行。但是在操作系统层面,它可能处于运行状态,也可能等待资源调度(例如处理器资源),资源调度完成就进入运行状态。所以该状态的可运行是指可以被运行,具体有没有运行要看底层操作系统的资源调度。

  • 阻塞(Blocked) - 此状态意味着:线程处于被阻塞状态。表示线程在等待 synchronized 的隐式锁(Monitor lock)。被 synchronized 修饰的方法、代码块同一时刻只允许一个线程执行,其他线程只能等待,即处于阻塞状态。当占用 synchronized 隐式锁的线程释放锁,并且等待的线程获得 synchronized 隐式锁后,就又会从 BLOCKED 转换到 RUNNABLE 状态。

  • 等待(Waiting) - 此状态意味着:线程无限期等待,直到被其他线程显式地唤醒。 阻塞和等待的区别在于,阻塞是被动的,它是在等待获取 synchronized 的隐式锁。而等待是主动的,线程通过调用 Object.wait 等方法进入。

  • 定时等待(Timed waiting) - 此状态意味着:无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒,线程通过调用设置了 Timeout 参数的Object.wait 等方法进入。

  • 终止(Terminated) - 线程执行完 run 方法,或者因异常退出了 run 方法。此状态意味着:线程结束了生命周期。

下面这张图更生动地展示了线程状态切换的occasion 和 trigger。

智能车竞赛飞檐走壁组立体赛道电磁引导线设计与实现 本文深入解析了智能车竞赛飞檐走壁组立体赛道电磁引导线的核心设计与实现。针对垂直面、圆筒、跷跷板等立体元素,详细阐述了电磁线规格参数选择、三维空间铺设技巧及磁场调试方法,旨在帮助参赛队伍打造稳定可靠的导航系统,提升车模在复杂立体赛道上的通过率与稳定性。 阅读详情

相关推荐

ENSP之配置静态路由

ENSP之配置静态路由 两台路由器两台PC 拓扑图如下: 配置AR1线路上的两条IP地址 配置AR2线路上的两条IP地址 3.AR2上 配置静态IP 路由 AR1上 配置静态IP 路由 设置两台PC 的IP地址 网关 PC1 PC2 全部配置好了之后 现在测试PC1是否ping 得通PC2 在命令行输入 成功的话如下图 ...

浪友与泪的博客 3545

JAVA--线程(2)

本篇用来测试线程常用的方法与关键字. sleep()与interrupt() static void sleep(long mills):使线程暂时进入"睡眠"状态,持续时间mills毫秒 boolean interrupt():中断线程. public class SleepTest extends Thread{ public static void main(String[...

bujiujie8的博客 406

NC65增加自定义参照并且参照数据要根据选择的财务组织过滤

1、首先定义一个参照类 package nc.pub.jzcm.ref; import nc.ui.bd.ref.AbstractRefModel; import nc.ui.bd.ref.IRefDocEdit; import nc.ui.bd.ref.IRefMaintenanceHandler; public class PMPrPayDefaultRefModel extends AbstractRefModel { public PMPrPayDefaultRefModel(){ ...

xyl的博客 3494

一个死锁问题分析

为什么会死锁呢?一起来看看!!!

码农练功房的博客 548

STM32N6570-DK ISP调试

将下载线接入STLINK V3EC接口,将BOOT1拨码拨至右边,即将其置为H,上电或已经上电的按复位,打开STM32CubeProgrammer,设置start address:0x70000000。板子消耗电流较大,即使缩短数据线接了USB供电依然不能正常点亮显示屏,最终通过板上CN2端子加了外部5V供电,仍然需要560mA的外部电流。3.烧录bin文件:STM32N6_ISP_IQTune_App_revC01-v1.1.0-trusted.bin。

liu的博客 1065

手把手教你用STM32F103C8T6驱动HUB75 LED点阵屏(附74HC595级联代码)

本文详细介绍了如何使用STM32F103C8T6开发板驱动HUB75接口的LED点阵屏,包括硬件连接、74HC595级联代码、时序控制及灰度显示实现。通过实战案例展示动态文字显示和性能优化技巧,帮助开发者快速掌握LED点阵屏驱动技术。

weixin_42623010的博客 342

Java阻塞的线程如何停止_Java多线程:如何停止/中断一个运行中的线程?

# 面试题:如何正确地停止/中断一个线程哪些情况下线程会停止如何处理不可中断的阻塞# 核心思想使用interrupt()来通知,而不是强制。# 代码演示场景1:run()方法中没有sleep()/wait()等会响应中断的方法。1.1 线程未处理中断:/*** 正确停止线程---run()方法内没有sleep()或者wait()方法-未处理中断信号** @author futao* @date ...

weixin_42680139的博客 837

宝塔面板全栈部署指南:Node后端+React前端与数据库配置实战

本文提供了一份详尽的宝塔面板全栈部署实战指南,涵盖Node.js后端、React前端与数据库的完整配置流程。文章深入解析了环境准备、数据库创建与导入、Node服务部署、PM2进程守护、React应用构建与托管,以及线上环境变量管理与安全配置等核心步骤,帮助开发者高效、稳定地将项目部署至生产环境。

weixin_29232121的博客 90

线程的调度(线程休眠,线程让步,线程插队)

程序中的多个线程是并发执行的,某个线程若想被执行必须要得到CPU的使用权,Java虚拟机会按照特定的机制为程序中的每个线程分配CPU的使用权,这种机制被称作线程的调度。两种线程调度模式:分时调度模式:让所有的线程轮流获得CPU的使用权,并且平均分配每个线程占用的CPU的时间片。抢占式调度模式:让可运行池中优先级高的线程优先占用CPU,而对于优先级相同的线程,随机选择一个线程使其占用CPU,当它失去...

田园园野的博客 4808

”COMSOL“竟然如此简单!我悟了!!

COMSOL Multiphysics具有强大的多物理场全耦合仿真分析功能、高效的计算性能,可以保证数值仿真的高度精确,已被广泛应用于各个学科领域,近年来运用COMSOL来解决电化学实际工程问题也越来越普遍。

C1012562281的博客 3448

51单片机入门指南-基于STC89C52(持续更新)

一、环境介绍 编程IDE: keil5 单片机型号: STC89C51/52 开发板: 普中科技的开发板 完整PDF资源下载地址:https://download.youkuaiyun.com/download/xiaolong1126626497/19785856 二、51单片机开发手册介绍 2.1 前言 51单片机是对所有兼容Intel 8031指令系统的单片机的统称。 该系列单片机的始祖是Intel的8004单片机,后来随着Flash rom技术的发展,8004单片机取得了长足的进展,成为应用最.

8798

再次出发:线程的休眠

线程的休眠 Thread.sleep(500);每500毫秒休眠一次 休眠后释放CPU,继续执行再次抢占cpu package com.wuzl; /** * 线程的两种实现方式 * 1、继承thread类 * 2、实现Runnable接口,推荐使用 */ /** * 线程的休眠 * Thread.sleep(500);每500毫秒休眠一次 * 休眠后释放CPU,继续执行再次...

雷爷的博客 427

【DRAM存储器一】基本存储单元、阵列结构、读写原理

详述DRAM存储器存储单元、存储阵列结构和读写原理

highman110的博客 7256

学完这篇并发多线程还会觉得心虚?

多线程入门知识

hspnb的博客 694

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

数学建模模型总结

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

java多线程技术_Java多线程技术

多线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上java内置的对多线程的天然支持,多线程编程不再是一道难以逾越的鸿沟。进程、线程、并发执行首先我们先来认识一下进程、线程、并发执行的概念:一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进...

weixin_30111277的博客 219

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

(转载) Java多线程技术

多线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上java内置的对多线程的天然支持,多线程编程不再是一道难以逾越的鸿沟。 进程、线程、并发执行 首先我们先来认识一下进程、线程、并发执行的概念:   一般来说,当运行一个应用程序的时候,就启动了一个进程,当然...

weixin_30418341的博客 92

Java多线程理解

多线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上java内置的对多线程的天然支持,多线程编程不再是一道难以逾越的鸿沟。 进程、线程、并发执行 首先我们先来认识一下进程、线程、并发执行的概念:   一般来说,当运行一个应用程序的时候,就启动了一个进程,当然

conect的专栏 1287

java多线程

线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上java内置的对多线程的天然支持,多线程编程不再是一道难以逾越的鸿沟。 进程、线程、并发执行 首先我们先来认识一下进程、线程、并发执行的概念:   一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有

liuzhe_2008的专栏 708

c语言tcp多线程聊天,基于tcp和多线程的多人聊天室-C语言

之前在学习关于网络tcp和多线程的编程,学了知识以后不用一下总绝对心虚,于是就编写了一个基于tcp和多线程的多人聊天室。具体的实现过程:服务器端:绑定socket对象->设置监听数->等待连接->有客户端连接就新建一个线程,这个线程中,一旦就收到这个客户发送的消息,就广播的向其他客户端发送同样的消息。客户端:向客户端连接->新建线程用来接收服务器端发送的消息,同时主进程用来...

weixin_42570484的博客 2751

面试被问高并发,又哑口无言?一次性把并发搞清楚!这样最靠谱

作为JAVA工程师 你曾经面对过多线程高并发这只怪兽吗? 你有没有被线程并发伤害过? 面对多线程与高并发 1、当面试官向你提问多线程高并发的实战问题时,你是否感觉到无所适从? 2、想解决生产环境中的线程池,你是否感觉到狗咬刺猬,无处下嘴? 3、面对锁的升级过程,你是否感觉到迷茫无助? 4、看过很多的多线程高并发的文章,但当遇到了生产环境中真实问题的时候,你是否依然不知道如何进行分析排查? 你缺乏这两个字 实战 消除痛点 解决面试 积累实战经验 欢迎你参加马士兵教育 JVM实战调优

文章中资料均可获取,有需要请添加yunduoa2019即可 536

JAVA多线程技术

Java多线程技术  --作者:杨帆         多线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上java内置的对多线程的天然支持,多线程编程不再是一道难

u013322876的博客 584

java多线程总结

今天编写多线程遇到了个问题,所以上网找了些java多线程的资料,现做一下总结。 -----------------------------------------------------------------------------------------   先来说一下我的问题: 父线程开启一个子线程,子线程中Thread.sleep(timeout),如果父线程没有退出,子线程可以从...

u011207577的专栏 177

Java中如何引用另一个类里的集合_2天4小时,20年Java架构师马士兵带你彻底掌握多线程与高并发技术...

多线程与高并发技术难在哪里?如果你平时只有CRUD的经验,从来不会了解多线程与高并发,相信你一定一头雾水。如果是这样,你一定要拿出4个小时的时间,参加一次马士兵老师的《多线程与高并发》训练营。让骨灰级扫地神僧马士兵老师带你将多线程的知识系统化,帮助你理解多线程在CPU层级的实现,以及这些实现如何一层一层的映射到那些上亿用户,千万QPS,百万TPS的系统。横扫一切关于多线程的问题,吊打所有敢于提问并...

weixin_39890652的博客 93

Java中的多线程编程解释概要

以下内容转自:http://blog.youkuaiyun.com/cqhweb/article/details/43735213 多线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上Java内置的对多线程的天然支持,多线程编程不再是一道难以逾越的鸿沟。

Monkey_D_Jie的博客 732

初识Java多线程

Java多线程技术  --作者:杨帆         多线程编程一直是学员们比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上java内置的对多线程的天然支持,多线程编程不再是一道难

Emrys的博客 347

多线程编程的基础知识点

http://www.blogjava.net/pengpenglin/archive/2008/09/02/226292.html 多线程编程一直是程序员比较头痛和心虚的地方,因为线程执行顺序的不可预知性和调试时候的困难,让不少人在面对多线程的情况下选择了逃避,采用单线程的方式,其实只要我们对线程有了明确的认识,再加上Java内置的对多线程的天然支持,多线程编程不再是一道难

Xiao_Qiang_的专栏 617
上一篇: 吃透JAVA的Stream流操作,多年实践总结
下一篇: 优雅的剥洋葱:浅析 Java Optional 类
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值