如何精简的理解Java中的多线程

芒果YOLOv8改进165:损失函数angleLoss:即插即用 | 独家首发原创新颖|公开数据集DOTA高效无损涨点0.5% | 旋转检测 YOLOv8改进:旋转检测Loss损失函数angleLoss:即插即用 | 独家首发原创新颖|公开数据集DOTA高效无损涨点0.5% 阅读详情

分为线程池处理Runnable任务 ,线程池处理Callable任务 和Executors的工具类构建线程池对象 ,我们将要深入性了解线程中的知识,主要是线程安全,线程同步,线程池三个知识点。

线程安全

首先我们应该了解的是什么是“ 线程安全 ”问题呢?通俗易懂的讲的话,那就是“假设在某地有一瓶水,石原里美和工藤静香都很渴,想要去喝这瓶水,然而当这两个线程同时启动的时候,二人都会去拿这瓶水,并且同时判断这瓶水是否还在?在这瓶水未被取走之前,二人的判断都是true,因此二人都能够取到这瓶水,可是明明只有一瓶水,却可以让两个人都取到水,这很明显是与现实生活中的情况是不符合的。”这就是我们需要处理的“线程安全”问题。

线程安全问题出现的原因:

  • 存在多线程并发
  • 同时访问共享资源
  • 存在修改共享资源

实战模拟

问题描述:

仍然是前面的例子,现有两人需要喝水,分别是石原里美和工藤静香,二者共享同一瓶水,若对方喝掉这瓶水,则另一方则没有水可以喝。

具体操作:

1、提供一个Account类并创建它,作为二人的共享水资源账户;

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public class Account {
    private int num;//代表水的数量

    public Account() {
    }

    public Account(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}</pre>

2、定义一个线程类,并且该线程可以处理Account对象;

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public class ThreadAccount extends Thread{
    private Account account;

    public ThreadAccount() {
    }

    public ThreadAccount(Account account) {
        this.account = account;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        if(account.getNum()>0){
            System.out.println(thread.getName()+"已经成功获得了这瓶水!");
            account.setNum(account.getNum()-1);
            System.out.println("此时还剩下"+account.getNum()+"瓶水");
        }else {
            System.out.println("水资源不够,已经无法取出");
        }
    }
}</pre>

3、创建两个线程,并传入同一个Account对象;

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public static void main(String[] args) throws Exception {
        Account account = new Account(1);
        ThreadAccount threadAccount1 = new ThreadAccount(account);
        ThreadAccount threadAccount2 = new ThreadAccount(account);
        Thread thread1 = new Thread(threadAccount1,"石原里美");
        Thread thread2 = new Thread(threadAccount1,"工藤静香");
        thread1.start();
        thread2.start();
    }</pre>

4、启动两个线程,同时获取同一瓶水。

通过运行结果,我们可以清晰的看出来存在很大的问题,当第一个人石原里美获得这瓶水的时候,水资源账户中已经没有水了,所以工藤静香是不能够取到水资源的,然而工藤静香仍然获得了水,且剩下了-1瓶水,这很明显是不符合现实情况的,那么我们该如何解决呢?

线程同步

为了能够解决刚才出现的问题,我们可以考虑使用线程同步,让多个线程实现先后依次访问共享资源。其核心思想是: 加锁 ,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来。通俗易懂的讲就是:“假如两个线程同时开始访问共享资源,在访问之前,谁先拿到钥匙打开这把锁,谁才能访问该共享资源。而另一个线程则只能在共享资源外等待钥匙空下来。”接下来介绍几种方式来解决该问题。

方式一:同步代码块

作用: 把出现线程安全问题的核心代码给上锁

原理:每次只能一个线程进入,执行完毕后自动解锁,其他进程才可以进来执行

<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">synchronized (同步锁对象){
            操作共享资源的代码(核心代码)
}</pre>

锁对象要求:理论上,锁对象只要对于当前同时执行的线程来说是 同一个对象 即可。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">synchronized ("喝水") {//喝水是对于两个线程的不变对象
            if(account.getNum()>0){
                System.out.println(thread.getName()+"已经成功获得了这瓶水!");
                account.setNum(account.getNum()-1);
                System.out.println("此时还剩下"+account.getNum()+"瓶水");
            }else {
                System.out.println("水资源不够,已经无法取出");
            }
        }</pre>

具体的操作就是将之前的run()方法中的核心代码加锁,即可实现多个线程依次访问共享资源

其中需要注意的一点是,同步锁对象等同于打开共享资源的一把钥匙,所以应该对多个线程是同一个对象,并且要规范命名,若同步锁对象命名相同,则会影响另一组线程对共享资源的访问。对于实例方法建议使用this作为锁对象;对于静态方法建议使用字节码(类名.class)对象作为锁对象。

方式二:同步方法

作用: 把出现线程问题的核心方法给上锁。

原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">修饰符 synchronized 返回值类型 方法名称(形参列表){
        操作共享资源的代码
}</pre>

与方式一的不同之处在于,前者是封装共享资源的核心代码,后者则是封装整个方法

<pre class="prettyprint hljs gradle" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public synchronized void LockWater(){
            if(account.getNum()>0){
                System.out.println(thread.getName()+"已经成功获得了这瓶水!");
                account.setNum(account.getNum()-1);
                System.out.println("此时还剩下"+account.getNum()+"瓶水");
            }else {
                System.out.println("水资源不够,已经无法取出");
            }
}</pre>

如果方法是实例方法:同步方法默认用this作为锁对象,但是代码要高度面向对象;

如果方法是静态方法:同步方法默认用类名.class作为锁对象。

方式三:Lock锁

  • 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活、方便
  • Lock实现提供比使用 synchronized 方法和语句可以获得更广泛的锁操作
  • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建锁对象

| 方法名称 | 说明 |
| public ReentrantLock( ) | 获得Lock锁的实现类对象 |
| void lock( ) | 获得锁 |
| void unlock( ) | 释放锁 |

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">//在使用之前先定义一个锁对象
private Lock lock = new ReentrantLock();
//定义过锁对象之后,即可以调用其API
lock.lock();
//共享资源代码
lock.unlock();</pre>

线程池

线程池是一个可以复用线程的技术;如果不使用线程池的话,用户每发起一个请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销很大,这样会严重影响系统性能。

线程池的实现原理:

在创建线程池时,设定该线程池固定存在N个 核心线程 用于处理任务,另外会有一个 任务队列 提供给任务排队等待,在任务队列中的前N个任务则是交给核心线程去处理,在没有空余线程的时候,其余任务则在任务队列中等待。

如何得到线程池对象?

  • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象;
  • 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程对象。

ThreadPoolExecutor构造器的参数说明

<pre class="prettyprint hljs cpp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)</pre>

参数一:指定线程池的线程数量(核心线程): corePoolSize (不能小于0)

参数二:指定线程池可支持的最大线程数: maximumPoolSize (最大数量>=核心线程)

参数三:指定临时线程的最大存活时间: keepAliveTime (不能小于0)

参数四:指定存活时间的单位(秒、分、时、天): unit (时间单位)

参数五:指定任务队列: workQueue (不能为null)

参数六:指定用哪个线程工厂创建线程: threadFactory (不能为null)

参数七:指定线程忙,任务满的时候,新任务来了怎么办: handler (不能为null)

为了更好的去理解多线程:我们可以假设 核心线程 数量为3个, 最大线程 数为5个,那么该线程池可以创建的 临时线程 数为5-3=2个线程,临时线程的最大存活时间是指其被创建之后不处理任务之后的存活时间,时间单位结合实际即可, 任务队列 设置为5个,参数六字面意思理解即可,参数七则是规定线程池不能再接收任务的时候如何处理。接下来通过这两个问题加深理解。

临时线程什么时候创建?

  • 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
  • (此时3个核心线程在处理任务,并且任务队列中已经有5个任务在等待了,然而仍然有任务过来,此时即可开始创建临时线程处理任务)

什么时候会开始拒绝任务?

  • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝。
  • (当3个核心线程和2个临时线程都在处理任务,且任务队列满的情况下,将会开始拒绝接收任务,由参数七决定该如何处理,由此我们也可以得出该线程池任务处理的最大数量为:3+2+5=10,即核心线程+临时线程+任务队列)

线程池处理Runnable任务

ThreadPoolExecutor创建线程池对象:

<pre class="prettyprint hljs lisp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());</pre>

ExecutorService常用方法

| 常用方法 | 说明 |
| void executor(Runnable command) | 执行任务/命令,没有返回值。一般用来执行Runnable任务 |
| Future<T>submit(Callable<T> task) | 执行任务,返回未来任务对象获取线程结果,一般拿来执行Callable任务 |
| void shutdown( ) | 等任务执行完毕后关闭线程池 |
| List<Runnable shutdownNow> | 立刻关闭,停止正在执行的任务,并返回队列中未执行的任务 |

参数七新任务拒绝策略

| 策略 | 说明 |
| ThreadPoolExecutor.AbortPolicy | 丢弃任务,并抛出RejectExecutionException异常,默认的策略 |
| ThreadPoolExecutor.DiscardPolicy | 丢弃任务,但是不抛出异常,不推荐 |
| ThreadPoolExecutor.DiscardOldPolicy | 抛弃队列中等待最久的任务,然后把当前任务加到队列中 |
| ThreadPoolExecutor.CallerRunsPolicy | 由主线程负责调用任务的run()方法从而绕过线程池直接执行 |

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">//main方法
public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,4,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        Runnable target = new MyThreadRunnable();
        //核心线程+任务队列=5,所以5个以内不需要创建临时线程
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        //当任务数量达到5个的时候,接收任务的时候就需要创建临时线程
        pool.execute(target);
        pool.execute(target);
        //当超过7个线程时,再次接收任务时,则会拒绝
        pool.execute(target);
    }
//实现Runnable接口
public class MyThreadRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"正在处理任务");
        Thread.sleep(10000000);//避免线程快速处理任务,无法达到实验效果
    }
}
//输出结果:
//pool-1-thread-2正在处理任务
//pool-1-thread-4正在处理任务
//pool-1-thread-3正在处理任务
//pool-1-thread-1正在处理任务</pre>

线程池处理Callable任务

| Future<T>submit(Callable<T> task) | 执行任务,返回未来任务对象获取线程结果,一般拿来执行Callable任务 |

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">//main方法
public class MoreThread {
    public static void main(String[] args) throws Exception{
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,4,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        Future<String> f1 = pool.submit(new MyThreadCallable());
        Future<String> f2 = pool.submit(new MyThreadCallable());
        Future<String> f3 = pool.submit(new MyThreadCallable());
        Future<String> f4 = pool.submit(new MyThreadCallable());
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}
//线程实现Callable接口
public class MyThreadCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return Thread.currentThread().getName()+"正在工作中~";
    }
}
//输出结果:仅由两个核心线程完成即可
//pool-1-thread-1正在工作中~
//pool-1-thread-2正在工作中~
//pool-1-thread-2正在工作中~
//pool-1-thread-2正在工作中~</pre>

Executors的工具类构建线程池对象

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。

| 方法名称 |

说明

|
| public static ExecutorsService newCachedThreadPool( ) | 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉。 |
| public static ExecutorsService newFixedThreadPool(int nThread) | 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。 |
| public static ExecutorsService newSingleThreadExecutor( ) | 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。 |
| public static ScheduledExecutorsService newScheduledThreadPool(int corePoolSize) | 创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 |

注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象。

<pre class="prettyprint hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">ExecutorService pool1 = Executors.newFixedThreadPool(5);
pool.execute(new MyThreadRunnable());
pool.execute(new MyThreadRunnable());
pool.execute(new MyThreadRunnable());</pre>

虽然Executors使用起来会很方便,但是仍然是存在风险的,因此还是推荐前面使用的线程池创建方式。

(1)FixedThreadPool和SingleThreadExecutor:

允许的请求队列长度为Integer.Max_VALUE,可能会堆积大量的请求,从而导致oom。

(2)CachedThreadPool和ScheduledThreadPool:

允许的创建线程数量为Integer.Max.VALUE,可能会创建大量的线程,从而导致oom

【同等学力】01-逻辑符号表达 F(x): x是确诊者,G(x):x有症状F(x): x是老人,G(x):x是宠物,L(x,y): x喜欢y。 阅读详情

相关推荐

解锁微信小程序Camera组件的隐藏技能:连续扫码背后的逻辑与陷阱

本文深入解析微信小程序Camera组件在连续扫码功能中的技术原理与优化方案,涵盖识别率提升、内存管理策略及典型问题排查。通过实战参数对比和代码示例,帮助开发者解决扫码枪应用中的性能损耗与界面卡顿问题,提升零售、物流等场景的工作效率。

weixin_29019241的博客 495

Java中的多线程如何理解——精简

通过前面的学习,我们已经学会了线程是如何创建的以及线程的常用方法,接下来呢,我们将要深入性了解线程中的知识,主要是线程安全,线程同步,线程池三个知识点。我相信大家通过这节课的简单地学习,就可以大概地掌握了线程吧!好了,废话不多说,我们开始今天的学习吧!......

weixin_50635856的博客 4632

你需要这样一个Ros软路由—— 路由器+外网+一号通ip线路=动态ip的WiFi信号

功能强大 管理便捷—给您全新的路由体验 一键更换指定线路or批量更换所有IP/MAC/SSID 自定义走本地网络策略的分流功能 设备批发价、不限制拨号账号服务商 强大的单设备/多设备IP去重功能 批量导入、管理一号通线路 一键任意切换5G_WiFi(推荐)/2.4G_WiFi(建议不用) 设备支持PPTP/L2TP/SSTP/openVP.N等协议 独家支持openVPN,适用于网络受限用户 支持自行接入AP和交换机扩容 一键打开/关闭所有虚拟WiFi 一键隐藏/显示设备WiFi...

米粒儿 4260

java中的线程如何理解——精简

经过前面的学习,我们已经了解到了IO流的知识,那么今天我们就将要开始学习Java中的线程,首先我们应该理解“线程”是什么意思?线程(thread)是一个程序内部的一条执行路径,我们所熟悉的main方法其实就是一条单独执行路径,若程序中只有一条执行路径那么这个程序就是单线程程序;既然有单线程,那么也相对的会有多线程,字面意思可以理解为“相对单线程从软硬件上多条执行流程的技术”,多线程的好处是提高CPU的利用率。 在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,大大提高程序的效率。.

weixin_50635856的博客 6301

基于睿抗智能小车省赛第一题兵人识别的学习5.25

先用pip uninstall numpy卸载,再在后面输入pip install numpy==1.23安装版本为1.23的numpy。首先安装Anaconda环境;pycharm方便使用,安装yolov5以及一大堆数据包;由于python中的numpy插件包版本过高,训练失败,所以要安装一个版本低的包。每次运行都要先运行虚拟环境,输入:conda activate。图中所有精度大概在0.768-0.99之间。这里指定安装numpy1.23.。训练完成会得到一个衡量精度的指标。等待99张图片训练完成。

Haili_的博客 1302

全双工AI语音交互:从原理到实践,构建实时对话系统

语音交互技术正从传统的半双工模式向全双工模式演进,以实现更自然、实时的对话体验。全双工交互的核心原理在于模拟人类电话通话,允许双方同时发送和接收音频流,这需要流式语音识别(Streaming ASR)和实时自然语言理解(Real-time NLU)技术的深度整合。其技术价值在于显著降低交互延迟,支持智能打断(Barge-in),从而提升人机对话的流畅度和自然度。在应用场景上,全双工技术是智能助手、实时翻译、车载语音及无障碍交互等领域的关键突破。本文以微软MAI Realtime等模型为例,探讨如何利用开源技

weixin_33693070的博客 405

java多线程详细理解

明确一点:多线程不是为了提高程序执行速度(性能甚至更低),而是提高应用程序的使用效率。 多线程的三大特性:原子性、可见性、有序性 一、创建线程 创建线程额的开销:分配内存 --> 列入调度 --> 线程切换的时候还要执行内存换页,CPU 的缓存被清空,切换回来的时候还要重新从内存中读取信息(破坏了数据的局部性) 创建线程的三种方式: 1.继承Thread(重点),重写run()方法,在main函数中,调用start()方法 2.实现Runnable接口(重点),重写run()方法,在main函数

YangGuang的博客 8902

【maxENT】从数据到地图:R语言实现最大熵物种分布建模全流程解析

本文详细解析了如何使用R语言实现最大熵模型(MaxEnt)进行物种分布建模的全流程。从数据准备、环境变量处理到模型训练与参数调优,再到模型评估与结果可视化,提供了实用的代码示例和避坑指南。特别适合生态学研究者利用最大熵模型处理物种分布数据,提升研究效率。

weixin_27244795的博客 216

Java中的网络编程如何理解——精简

经过前面的课程,我们已经大概地了解到多线程的知识了,不知道大家是否已经掌握了呢?如果已经掌握了的话,那么我们今天就开始学习Java中的网络编程的相关知识,那么什么是网络编程呢?网络编程是可以让程序与网络上的其他设备中的数据进行数据交互。常见的通信模式有两种形式:Client-Server(CS)、Browser/Server(BS)。接下来我们将要学习的知识是网络通信的三要素、UDP通信、TCP通信。......

weixin_50635856的博客 1916

万字数据仓库面试题及参考答案

星型模型由一个中心的事实表和多个外围的维度表组成,事实表存储度量值和事实,维度表存储描述性属性和维度。星型模型和雪花模型的区别在于雪花模型将维度表进一步细化,形成多层次的结构,而星型模型由一个中心事实表和多个维度表组成,适用于查询较为简单的情况。维度建模通常采用星型模型或雪花模型,将数据分为事实表和维度表,其中事实表存储度量值和事实,维度表存储描述事实的属性。三者之间的关系可以这样理解:数据仓库主要关注历史数据的存储和分析,数据中台则关注数据的服务化和资产化,而数据湖则关注原始数据的存储和多样性。

大模型大数据攻城狮的专栏 560

《深入浅出多模态》(二):多模态任务及数据集介绍

本文为《深入浅出多模态》系列第二章,《多模态任务介绍与背景》主要介绍多模态模型的概念、技术难点、下游任务(Visual Grounding、VQA、image/Video Captioning等)及各类应用方向数据集展开介绍,本专栏适合从事多模态小白及爱好者学习,欢迎大家关注,如有侵权请联系删除!

专注大数据与人工智能技术分享,欢迎私信加群互相学习! 3836

理解 Java 中的多线程编程

多线程编程是 Java 开发中的一项重要技能,能够显著提高程序的性能和响应性。本文从基础概念入手,详细讲解了 Java 中的多线程实现方式、线程同步与通信、线程池、并发集合以及死锁与活锁的避免方法,并通过实际案例加深理解。希望本文能够帮助新手朋友全面掌握 Java 多线程编程,提升开发技能。

这家伙很懒,什么都没有留下 1058

【vsomeip3 教程】SOME/IP中间件去抖策略全解析

本文探讨了在SOME/IP中间件中实现Debounce(去抖动)机制的必要性和实现原理。文章首先分析了中间件层进行Debounce的四大动机:多订阅者共享收益、语义显式化、协议噪声屏蔽和入口处背压控制。接着详细阐述了Debounce的底层原理,包括变化检测机制(ignore和mask的应用)、时间门控策略(interval的两种时间基准)以及超时补发机制,并通过表格清晰展示了不同参数组合的效果和适用场景。最后,文章提出了工程落地的具体方法,包括策略选型、ignore/mask的设计方法论以及分层验证调试的实

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 232

深入理解Java中的多线程与同步

在计算机领域,多线程是指一个进程(程序的执行单元)中包含多个执行流。这意味着程序可以同时执行多个任务,提高了程序的并发性和响应性。Java通过java.lang.Thread类和java.util.concurrent包提供了丰富的多线程支持。

qq_28068311的博客 1289

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Java中的多线程,这样说你更能理解。

大家已经熟悉了,在听着QQ音乐的同时,一边打着英雄联盟,,同时还挂着qq,时不时的被腾讯电脑管家弹出弹窗骚扰一下日常生活,这是因为我们操作系统的一种能力,看起来是在同一时刻运行多个程序,实际上不是,随着电子信息技术的不断发展,现在电脑都是拥有多个CPU的计算机,但是并发执行的进程并不受限于CPU的数目。操作系统会为每个进程分配CPU时间片,给人并行处理的感觉,就像白炽灯泡在一秒钟亮灭50次一样,我们人眼是察觉不到的,计算机一秒钟运行上亿乃至如今的超级计算机运行百万亿次。我们可以查看自己的电脑是多少核心,多少

2401_83916355的博客 759

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

JAVA多线程理解

线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。多线程程序应用能够在同一时间执行多于一个任务,进而提升整体处理性能。本文将从最基础的部分开始讲解JAVA线程的知识,最后深入了解JAVA多线程的高级应用以及优化。

qq_22610595的博客 823

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Java基础——深入理解Java中的多线程(超级详细,值得你看)

Java中多线程,深入理解

haust_允谦的博客 4194

如何理解Java多线程?

线程和进程 进程:是一个正在执行的程序 每个进程执行都有一个执行顺序,改顺序是一个执行路径,或者叫一个控制单元 线程:就是进程中的一个独立控制单元,线程控制着进程的执行 一个进程中至少有一个线程 多线程:CPU轮流执行每个进程(我们所谓的同时启动几个APP,看上去是同时运行,其实CPU在进程之间做着高速切换) Java中的多线程 如何在自定义的代码中,自定义一个线程呢? 查找API,发现Java中...

bigestboss的博客 772

深入理解Java中的多线程与并发控制

Java的多线程和并发控制是一个复杂但强大的领域,掌握它对于开发高性能、高响应性的应用程序至关重要。通过本文的介绍,我们了解了Java多线程的基础、线程同步与通信的机制,以及并发控制中常用的工具和方法。Java作为一门广泛使用的编程语言,其强大的并发支持机制使得它成为处理并发任务的理想选择。本文将深入探讨Java中的多线程基础、线程同步与通信机制、以及并发控制中常见的工具和方法。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。线程间的通信主要通过共享对象上的锁来实现。

Xiaoxin的博客 1446

记录Java多线程理解

记录了我在学习多线程的时候的一些看法。

想看什么文章都可以私信给我,综合性博客。 924

一分钟教你理解什么是多线程

我们都知道,如果程序在没有跳转语句的前提下,都是按照程序的编写顺序由上至下依次执行的,那假如现在想让你设计一个程序,可以边打游戏边听歌,那么我们该怎么设计呢? 如果你不知道多线程的概念的话,肯定是没有办法设计的,但是我们平常生活中,多线程无处不在,手机,电脑都有多线程的体现。因此,如果要解决上述问题,咱们得使用多进程或者多线程来解决. 1. 并发与并行 并发:指两个或多个事件在同一个时间段内发...

swordsmanye的博客 4232

【Java】深入理解Java中的多线程同步机制

多线程同时读写共享变量时,可能会造成逻辑错误,因此需要通过synchronized 同步;同步的本质就是给指定对象加锁,加锁后才能继续执行后续代码;注意加锁对象必须是同一个实例;对 JVM 定义的单个原子操作不需要同步。

weixin_71491685的博客 1369

Java 多线程理解

1.理解多线程 首先我们要了解什么是串行、并发、并行 串行:一个线程执行到底,相当于单线程。 并发:多个线程交替执行,抢占cpu的时间片,但是速度很快,在宏观角度看来就像是多个线程同时执行。 并行:多个线程在不同的cpu中同时执行。 并发与并行的区别: 并发严格的说不是同时执行多个线程,只是线程交替执行且速度很快,相当于同时执行。 而并行是同时执行多个线程,也就是多个cpu核心同时执行多个线程。 在实际开发中,我们不需要关心是否是并发还是并行,因为cpu会帮我们处理多线程,开发中可以认为多线程就是同时执行多

m0_46591169的博客 307

对Java多线程在Netty中的应用的理解

对共享的可变数据同步经常使用Java的老司机都知道,synchronized关键字可以保证同一时刻只有一个线程执行某个方法或者代码块。同步的作用不仅仅是互斥,也是共享可变数据。当一个线程修改可变数据并释放锁后,其他线程可以获得修改后的新值。 下面以ServerBootstrap类为例来分析,该类的初始变量有如下代码:private final Map<ChannelOption<?>, Objec

wangshuang1631的博客 2283
上一篇: Java绘机制和绘图演示
下一篇: 只要一个依赖给Swagger换上新皮肤,帅呆了!
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值