多线程——Thread类及常见方法

【多线程】Thread类 本期主要讲解: 1.Java 中如何进行多线程编程? 2.简单使用多线程 3.start和run的区别 4. jconsole 工具 5.Java中创建线程的写法 6.Thread类方法介绍 6.1构造方法 6.2 Thread类常见属性 6.3什么是守护线程? 6.5什么情况线程才是存活的? 6.6什么是线程中断? 阅读详情

这一篇我们就来了解一下,具体的Thread类的用法和使用场景。相信大家经过对多线程的一定认识都明白线程是需要创建的,接下来我们一起来详细学习Thread类。

Thread的常见构造方法

方法说明
Thread()
创建一个线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象并命名
Thread(Runnable target, String name)
使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,
Runnable target)
线程可以被用来分组管理,分好的组即为线程组,这
个目前我们了解即可

上面就是常用的Thread类的构造方法,并不是很难,需要大家进行相应的练习,去记住如何创建Thread类对象即可。

class myRunnable1 implements Runnable {

    @Override
    public void run() {
        System.out.println("我是线程二");
    }
}

class myRunnable2 implements Runnable {

    @Override
    public void run() {
        System.out.println("我是线程四");
    }
}

public class demo9 {
    public static void main(String[] args) throws InterruptedException {
        // 创建线程对象
        Thread t1 = new Thread();
        Thread t2 = new Thread(new myRunnable1());
        Thread t3 = new Thread("我是线程三");
        Thread t4 = new Thread(new myRunnable2(), "我是线程四");

        // 开启线程
        t1.start();
        Thread.sleep(1000);
        t2.start();
        Thread.sleep(1000);
        t3.start();
        Thread.sleep(1000);
        t4.start();
        Thread.sleep(1000);

        System.out.println("我是主线程");
    }
}

Thread的常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否有后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

上面是我们开发当中常用的Thread类常用的属性,我们来挨个介绍一下他们使用的场景和使用原因。

ID

ID是一个十分好用的东西,我们每个人都有自己的身份认证,在计算机的世界里面也是如此,我们既然创建了一个线程,那在计算机的世界里也会有属于他的身份认证。它可以让我们在进行调试线程的时候起到巨大的帮助,当我们创建一个线程的时候,JVM就会帮我们自动分配一个唯一的正整数作为这个线程的标识符,我们可以通过这个标识符来对这个线程进行调试、日志记录和线程管理。

它具有很强的线程安全性,getId()方法是Thread类的非静态方法,他操作的是调用它具体的线程对象,在多线程的使用环境下也不用担心返回的线程ID是错乱的。

public class ThreadgetId {
    public static void main(String[] args) {
        // 创建两个线程
        Thread thread1 = new Thread(() -> {
            long id = Thread.currentThread().getId();
            System.out.println("Thread 1 ID: " + id);
        });

        Thread thread2 = new Thread(() -> {
            long id = Thread.currentThread().getId();
            System.out.println("Thread 2 ID: " + id);
        });

        // 开启线程
        thread1.start();
        thread2.start();
    }

}

名称

讲完了ID,接下来我们再来讲一讲名称的使用,其实跟ID的用意很类似,但开发人员能更直观地看到每个线程的差异。就好比我们每个人都有自己的名字,也会有自己的身份证,国家可以通过身份证的信息来找到我们具体的活动轨迹,在计算机世界里,我们使用getName()方法获取的线程名称是和线程的ID绑定在一起的,但和我们现实生活不同,我们每个人的名字可能会出现有重名的可能,所以需要我们使用身份证号码来给每个人进行身份认证,但如果你在一个程序中给两个及以上的线程都命名一样的名称,会在我们去进行调试和管理上面耗费大量的时间,在我们实际的开发过程中,保证线程名称的唯一性。

public class ThreadgetName {
    public static void main(String[] args) throws InterruptedException {
        // 使用lambda表达式创建多线程
        Thread t1 = new Thread(() -> {
            System.out.println("我是线程一");
        },"线程一");

        Thread t2 = new Thread(() -> {
            System.out.println("我是线程二");
        },"线程二");

        // 获取名称
        System.out.println("Thread 1 name: " + t1.getName());
        System.out.println("Thread 2 name: " + t2.getName());

        t1.start();
        t2.start();
        Thread.sleep(1000);
        t1.join();
        t2.join();
        
        // 重新设置线程名称
        t1.setName("我现在是线程二");
        t2.setName("我现在是线程一");
        System.out.println("Thread 1 name: " + t1.getName());
        System.out.println("Thread 2 name: " + t2.getName());
    }
}

状态

线程状态是描述了一个线程在生命周期中的不同状态。

public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {    
            // 展示线程状态枚举常量
            System.out.println(state);
        }
    }
}

Java中的线程状态是通过 Thread.State 枚举类来表示的,主要包括以下几种状态:

NEW(新建状态):当线程对象刚刚被创建但尚未启动时,它处于新建状态。

RUNNABLE(可运行状态):线程可以运行,可能正在等待 CPU 时间片(在操作系统层面),也可能正在执行任务。

BLOCKED(阻塞状态):线程因为等待监视器锁(synchronized关键字锁定的对象)而被阻塞,其他线程持有锁,当前线程无法获取锁时会进入这种状态。

WAITING(等待状态):线程无限期地等待另一个线程来执行某一特定操作。例如,调用 Object.wait() 方法或者 Thread.join() 方法而没有指定超时时间。

TIMED_WAITING(计时等待状态):线程等待另一个线程来执行操作,但是等待有超时限制。例如,调用 Thread.sleep() 方法或者 Object.wait() 方法。

TERMINATED(终止状态):线程已经执行完毕,不再活动。

接下来我来详细讲一下这几种状态在实际线程运作时的表现情况:

public class ThreadStateTransfer {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                // 给线程一个代码执行
                for (int i = 0;i < 1000000;i++) {

                }
            }
        },"李四");

        System.out.println(t.getName() + ":" + t.getState());
        // 开启线程
        t.start();
        // 如果线程存活,那就执行sout语句
        while (t.isAlive()) {
            System.out.println(t.getName() + ":" + t.getState());
        }
        // 打印线程执行完代码状态
        System.out.println(t.getName() + ":" + t.getState());
    }
}

上述代码运行就说明了线程状态在其生命周期执行的一般过程,首先线程在没有开启的时候是NEW状态,代表已经被创建出来,但没有执行代码,是未启动的状态。

等到我们开启线程后,线程开始执行run()方法中的代码块,在我们的线程状态中就是正在运行,也就是RUNNABLE。

当我们在运行代码块的时候,用isAlive()方法判断线程是否执行完所有的代码块,如果执行完后就跳出循环,再打印线程状态,我们会发现显示的是TERMINATED,代表线程已经执行完毕。但这不意味着线程的生命周期到这一步就彻底结束,在我们目前的代码看来,这个我们创建的线程确实是完成了他的任务,但在我们以后的企业开发当中,可能会有线程池之类的操作,会把线程重新利用起来执行其他任务的代码块,那这个时候就不是死亡,而是新生,这也是为什么制定线程的大佬没有将线程任务执行完毕的状态常量设置为DEAD。

public class ThreadStateTransfer2 {
    public static void main(String[] args) {
        final Object object = new Object();

        // 创建一个执行Thread睡眠的线程
        Thread t1 = new Thread(() -> {
            // 将执行代码块上锁,查看线程状态
            synchronized (object) {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"t1");

        // 开启t1线程
        t1.start();
        // 测试能不能执行到t2线程的代码块
        Thread t2 = new Thread(() -> {
            synchronized (object) {
                System.out.println("ko no dio da!");
            }
        },"t2");
        
        t2.start();

        // 创建一个主线程来执行代码块
        while(true) {
            System.out.println("看看我运行了没有~");
        }
    }

}

但我们不能直观的感受到TIMED_WAITING和BLOCKED的线程状态,因为你在主线程中使用getState()方法获取到的线程状态只有未运行、正在运行和运行完毕这三种,因为在主线程中,你创建的线程正在执行代码块的编译,而不是本身线程的状态,也可以理解成你的老板只会看你人有没有来上班,也就是没来、来上班了和下班,但只有你自己知道身体舒不舒服,要不要和老板请假之类的~所以说我们在主线程中获取我们自己创建的线程状态并不是线程本身的状态,而是在主线程里的状态~

那我既然说了这么多,友友们一定就在想,难道没有更好的办法去获取TIMED_WAITING和BLOCKED的线程状态了吗?那当然是有的!而且还是可视化界面,可以很直观的让我们开发人员感受到线程状态的变化~

我们在我们JDK的版本jar包中找到一个叫jconsole.exe的程序,这个就是观察线程使用的监控管理工具,它不仅是显示线程状态的,还是监视JVM和Java应用程序的工具,我们可以在里面来查看JVM的运行状态、内存使用情况和线程信息等等,是一款十分好用的可视化工具。

我们还可以在我们的命令行里输入jconsole直接打开,效果是一样的。

打开以后我们会发现上面会显示我们的本地进程,我们要找到我们想要监视和管理的Java进程。

我们已经在后台运行了我上面的示例代码,就会在本地进程中找到对应的进程名称,这时候我们就可以点击连接去查看这个进程内的所有线程了!我们直接选择不安全的连接去查看线程即可。

点击线程就可以看到我们在ThreadStateTransfer2 上所有的线程了,我们来看看有哪些重要的线程把!

我们可以看到在我们创建并运行的Java程序中有正在执行的main线程,它一直在打印字符,所以它的线程状态是RUNNABLE。

我们接着再来看我们创建的t1线程,我们会发现它自身是一个TIMED_WAITING的状态,代表着我们这个线程正在执行Thread.sleep(1000)这串代码,这串代码会让我们的线程处于一个等待的时间,但这个等待时间并不是其它线程没有执行完,而是说需要过1000毫秒才能去执行其他命令。

我们拿生活中的例子来比较,比如说老板给你派了一项任务,让你必须今天把这个方案写出来,那你是不是拼命往死里写,不然可能就被炒鱿鱼了呀,但突然写着写着,肚子开始痛起来了,你才意识到中午吃的便宜外卖不干净,你急忙去上厕所,结果这个时候老板看看你任务完成的怎么样了,结果就发现你的工位是空的,老板打电话问你什么情况,你这时候就是一个TIMED_WAITING的状态,你只好告诉老板现在拉肚子,我需要上完厕所后才能继续完成任务,也就是有时间限制的等待~

我们再来看t2线程的状态,我们可以发现它处在一个BLOCKED的状态,为什么会造成这样的情况呢?这里其实和锁竞争有一定的关系~因为我们在代码中添加了一个叫synchronized的关键字(这个之后会详细介绍),他把我们的线程锁了起来,我们这里用一个定义的Object对象来表示一把锁,t1和t2都用的是同一把锁,当t1线程还没有结束,一直在死循环的时候,我们的t2线程只能乖乖的等t1执行完毕后才能执行自己的代码块,因为他们用的是同一把锁,现在只有t1有钥匙,而t2没有,所以就会出现阻塞的状态,也就是BLOCKED。所以我们可以看到工具上面显示拥有者是t1线程就是这个原因~

还是用生活中的例子来讲,接着上文,你们公司的小李这时候也是肚子疼,也是想来上厕所,但你们老板很抠门,整个公司十几个部门,就一个坑位!这时候小李就拍门说让你快点,他已经小荷才露尖尖角了,这个时候你就是t1线程,而可怜的小李就是t2线程,他必须等你上完了才能去释放,这个过程就是小李被你阻塞了,也就是BLOCKED~

public class ThreadStateTransfer3 {
    public static void main(String[] args) {
        final Object object = new Object();

        // 创建一个执行Thread睡眠的线程
        Thread t1 = new Thread(() -> {
            // 将执行代码块上锁,查看线程状态
            synchronized (object) {
                while (true) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"t1");

        // 开启t1线程
        t1.start();
        // 测试能不能执行到t2线程的代码块
        Thread t2 = new Thread(() -> {
            synchronized (object) {
                System.out.println("ko no dio da!");
            }
        },"t2");

        t2.start();

        while(true) {
            System.out.println("看看我运行了没有~");
        }
    }
}

我们再创建一个class,让我们把Thread.sleep(1000),变成object.wait(),再来查看我们的线程状态会不会有所改变,打开我们的jconsole窗口看看是个啥情况~

我们发现因为线程t1调用 object.wait() 来使线程进入等待状态(WAITING 状态),这意味着线程t1陷入了一个没有时间限制的等待状态,我相信各位友友们应该也理解了TIMED_WAITING和WAITING的区别,如果再拿我们举例子的话,那就只能一直在厕所便秘直到活活憋死,多少有点不人性了哈~
 

优先级

说到优先级,我相信各位友友们都能明白是啥意思,优先级高的那肯定就是优先享有权限,就还是可怜的小李,如果恰好这天没有你和他抢厕所,他正准备进去,一抬头就发现老板也想上厕所,这个时候小李自然是不敢抢占老板上厕所的,这也正是因为老板的优先级比小李高,如果是你和小李抢厕所,那真就是比谁更有参加男子一百米决赛的实力了~因为你和小李都是这个公司最底层的小社畜,你们的优先级是一样的~

public class ThreadPriority {
    public static void main(String[] args) {
        // 创建三个线程
        Thread t1 = new Thread(() -> {
            System.out.println("我是爷爷!");
        },"爷爷");
        Thread t2 = new Thread(() -> {
            System.out.println("我是爸爸!");
        },"爸爸");
        Thread t3 = new Thread(() -> {
            System.out.println("我是儿子!");
        },"儿子");

        // 查看当前线程优先级
        System.out.println(t1.getPriority());
        System.out.println(t2.getPriority());
        System.out.println(t3.getPriority());
        
        // 开启线程
        t1.start();
        t2.start();
        t3.start();
        
    }
}

我们可以看到在没有手动设置优先级的情况下,我和爸爸还有爷爷都是同辈的,那肯定不行,不然你爸爸还是你爸爸吗,你爷爷还是你爷爷吗?我们需要进行一些小小的修改。


class old implements Runnable {

    @Override
    public void run() {
        while(true) {
            System.out.println("还是"+Thread.currentThread().getName()+"技高一筹!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadPriority {
    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();

        // 创建三个线程
        Thread t1 = new Thread(new old(),"爷爷");
        Thread t2 = new Thread(new old(),"爸爸");
        Thread t3 = new Thread(new old(),"儿子");

        // 设置每个线程的优先级
        t1.setPriority(Thread.MAX_PRIORITY);
        t2.setPriority(Thread.NORM_PRIORITY);
        t3.setPriority(Thread.MIN_PRIORITY);

        // 查看当前线程优先级
        System.out.println(t1.getName() + "优先级是" + t1.getPriority());
        t1.start();
        System.out.println(t2.getName() + "优先级是" + t2.getPriority());
        t2.start();
        System.out.println(t3.getName() + "优先级是" + t3.getPriority());
        t3.start();

    }
}

这时我们就可以发现我们的优先级发生了改变,死循环打印数据会发现爷爷在第一个的概率是最高的。

那是不是意味着优先级就是无敌的呢?当然不是,如果小李实在忍不住了,他也一样不会让着老板,肯定自己先舒舒服服去释放了~所以说线程优先级的确是一个指示,而不是强制性规定。具体的线程调度行为依赖于操作系统和JVM的具体实现。高优先级的线程通常会更频繁地被调度执行,但并不保证它们一定会在低优先级线程之前完成。所以我们在平常使用的时候一定要慎重去使用优先级来控制线程的行为,他可能会导致不可预料的结果。

所以那种只有一个厕所的公司老板真的有存在的必要吗?

后台线程

说完了优先级,我们再来说说后台线程。


public class ThreadDaemon {

    public static void main(String[] args) {
        Thread normalThread = new Thread(new NormalTask());
        Thread daemonThread = new Thread(new DaemonTask());

        // 将daemonThread设置为后台线程
        daemonThread.setDaemon(true);

        // 启动线程
        normalThread.start();
        daemonThread.start();

        System.out.println("主线程准备退出");
    }

    static class NormalTask implements Runnable {
        public void run() {
            try {
                Thread.sleep(5000); // 模拟任务执行5秒钟
                System.out.println("普通任务已完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class DaemonTask implements Runnable {
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000); // 模拟后台任务每秒执行一次
                    System.out.println("后台任务正在执行中");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在这个示例中,创建了两个线程 normalThread 和 daemonThread,分别执行普通任务和后台任务。daemonThread 被设置为后台线程(通过 setDaemon(true) 方法),而 normalThread 是普通线程。在主线程中,先启动了这两个线程,然后主线程输出一条信息表示即将退出。

由于 daemonThread 是后台线程,当所有的非后台线程(这里指 normalThread)完成后,程序会自动退出,并且 daemonThread 的执行也会随之停止。在示例中,daemonThread 每秒输出一条信息,直到程序退出。

这种后台线程通常用于执行一些不需要在应用程序退出时手动关闭的任务,比如监控、日志记录等。

中断

终于来到最后一个内容,不容易不容易,那我们最后就好好讲一讲什么是中断。


public class  interrupteddemo1{
    private static final Object lock = new Object();
    private static volatile int isQuit = 0;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    while (true) {
                        lock.wait(); // 等待被唤醒
                        if (isQuit == 1) {
                            System.out.println("t1 退出!");
                            break;
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {
               while (true) {
                System.out.println("请输入isQuit:");
                Scanner scanner = new Scanner(System.in);
                isQuit = scanner.nextInt();

                synchronized (lock) {
                    lock.notify(); // 唤醒等待的线程
                }
            }
        });

        t1.start();
        t2.start();
    }
}

我们中断一个线程最常见的有两种方式:

1. 通过共享的标记来进行沟通
2. 调用 interrupt() 方法来通知
上面的示例代码就是通过共享的标记进行线程的停止的,我们需要将标识位加上volatile关键字(这个以后会详细介绍)
我们也可以使用Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定
义标志位。Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记。
方法说明
public void interrupt()
中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,
否则设置标志位
public static boolean
interrupted()
判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean
isInterrupted()
判断对象关联的线程的标志位是否设置,调用后不清除标志位
public class interrupteddemo2 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println("线程正在运行");
                    Thread.sleep(1000); // 可能被中断的阻塞操作
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 重新设置中断标志位
                System.out.println("结束线程");
            }
        });

        t.start();

        // 主线程等待一段时间后中断 t2 线程
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t.interrupt(); // 请求中断 t2 线程
    }
}

在这个示例中,t线程在 Thread.sleep() 中阻塞,主线程调用 t.interrupt() 后,t线程会抛出 InterruptedException,然后在异常处理中重新设置中断标志位,这样线程就能够响应中断并退出。

说实话效果是一样滴,但不过下面这种比较适合懒人,咳咳,开个玩笑,我们不仅要掌握Thread方法,也要明白它背后的原理是什么!

结尾

到了结尾讲一讲Thread当中的join()方法,其实我觉得start()方法和sleep()方法没必要讲,相信各位友友们跟着我的示例代码进行练习也可以很快明白其含义~

public class joindemo {
     // 此处定义一个int类型的变量
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0;i < 50000;i++) {
                count++;
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count++;
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2. join();

        // 预期结果应该是10w
        System.out.println("count:" + count);

        // 上述这样的情况就是典型的线程安全问题!!!
    }

}

我们会发现,在上面的示例代码中每次计算出来的结果都不一样这是为什么呢?

因为我们的count变量是一个静态变量,会被多个线程同时进行访问和修改,这就可能导致当t1线程还在将数据录入进去的时候,t2线程已经录入完毕,并修改了count的储存在内存中的值,那t1老弟肯修改的值肯定就不算数了呀,因为它没有把数据保存到内存当中,那这个数据就被浪费掉了,结果就是缺失了一次t1线程老弟修改count++的机会。有没有感觉就跟你上学时期有喜欢的人,当你还在通宵写情书,刚刚把情书放在他/她的抽屉里,就看到他/她接受了另一个人的表白,那你觉得你的这封信还有存在的必要吗?好像说多了都是眼泪,不过计算机的世界同样也是绝情绝义~

这就是线程存在非原子的操作导致执行过程不是原子性,不能将count的值正确的保存到内存当中。

那就有友友问了,博主博主,原子性是什么东西?我在这边浅浅的讲一下,之后在关于线程安全问题上面我会进一步阐释原子性的重要性,原子性是指一个操作是不可分割的,要么全部执行成功,要么全部不执行,中间不会被打断。

为了保证我们的程序是确保原子性的,我们就需要采取一些强硬的手段来完成!

我们就可以使用join()方法来确保原子性!

public class joindemo {
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
               count++;
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                count++;
            }
        });

        t1.start();
        t1.join();
        t2.start();
        t2.join();

        // 预期结果应该是10w
        System.out.println("count:" + count);
    }
}

这样出来的数据就是具有原子性的,那为什么会是这个样子的捏,其实道理很简单,聪明的友友估计已经看出些许端倪了,我们把join()方法的位置调整了一下,join()方法的意义就在于等待一个线程结束,在我们没有修改的示例当中,我们的t1和t2是一起开启的,但它们的执行是不可预料的,就可能会发生上面横刀夺爱的情况,这当然是谁都不想看到的情况,那我们就等一个线程全部执行完后再去执行另一个线程的代码块,这样就避免的非原子性的可能发生。

当然还有很多方法,比如使用同步机制(synchronized)或者使用原子类等方法也可以完成上述的操作,当然这些具体的知识等到之后我也会详细的进行讲解~

剩下的join()方法的其他用法就等着各位自行探索了,我把方法使用说明放在下面!

方法说明
public void join()
等待线程结束
public void join(long millis)
等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)
同理,但可以更高精度

遥感生态指数(RSEI)——四个指数的计算 遥感生态指数是在4个生态指数的基础上通过主成分变换得出的一个评价生态环境质量的指标。因此,第二部分就是要分别计算出这4个指数的值。 ①首先,是绿度指数的计算,一般用归一化植被指数(NDVI) NDVI=(NIR-Red)/(NIR+Red) 在Bandmath工具中输入NDVI计算公式 (float(b4-b3))/(b4+b3) 其中,b3、b4分别是红波段和近红外波段 ②其次,是湿度计算。 WET = c1B1 + c2B2 + c3B3... 阅读详情

相关推荐

从零开始:用摩尔线程S4000运行开源AI项目的完整流程

本文详细介绍了如何使用摩尔线程S4000 GPU从零开始运行开源AI项目的完整流程,包括硬件选择、驱动安装、容器化环境配置以及典型AI项目的实战部署。通过性能优化技巧,显著提升推理速度,适合开发者快速上手国产GPU的AI应用开发。

weixin_29231725的博客 582

多线程thread

Java.Thread(多线程) 线程就是独立的执行路径 在线程运行,即使没有自己创建线程,后台也会有多个线程,如主线程,gc(垃圾回收)线程 main称之为主线程,为程序的入口,用于执行整个程序; 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预。 献丑操作时会带来额外的开销,如cpu调度时间,并发控制开销 线程创建 方法一: ​ 1.定义一个类继承Thread接口 ​ 2.重写这个接口的run()方法; ​ 3.使用(实例化该方法) ​

c_chuxuezhe_的博客 312

Apifox CLI + Claude Skills:将接口自动化测试融入ai

基于 Apifox CLI、Claude Code以及Claude Skills组合,实现自然语言触发接口自动化测试。

wwz_0012的博客 599

多线程——Thread类

Thread类 线程状态代表一个线程在生命周期中所处的状态或者阶段。 public class Thread implements Runnable { public enum State { 用new创建了之后,但是还未start运行 NEW, 正在执行状态 RUNNABLE, 阻塞状态 ...

dgh112233的博客 383

新手也能懂的Simulink汽车二自由度建模:从公式推导到模型搭建全流程

本文为新手提供了在Simulink中搭建汽车二自由度模型的完整实战指南。文章从模型核心概念讲起,详细讲解了如何将车辆动力学方程转化为状态空间模型,并逐步指导在Simulink中实现从参数设置、模块搭建到仿真验证的全过程。通过阶跃转向测试和参数影响分析,帮助读者直观理解车辆转向特性,并提供了实用的模型扩展思路与常见问题排查技巧。

weixin_29047919的博客 215

十三、FLUENT蒸发/冷凝模型实战:从沸腾模拟到常温蒸发的UDF进阶

本文深入探讨FLUENT蒸发/冷凝模型的应用实践,从沸腾模拟到常温蒸发的UDF进阶。通过分析相变模型的核心逻辑、关键陷阱及UDF动态饱和温度场的实现,帮助工程师准确模拟不同场景下的蒸发/冷凝过程。结合太阳能蒸馏器等工程案例,提供优化方案和常见问题解决方案,显著提升模拟精度与效率。

weixin_42548893的博客 571

多线程(Thread)

多线程

qq_37107017的博客 598

博图Advanced实战:VB脚本实现HMI画面智能弹出(附完整代码解析)

本文详细介绍了在西门子博图Advanced平台中利用VB脚本实现HMI画面智能弹出的实战方法。通过动态计算坐标、分辨率自适应方案和完整代码解析,帮助工程师解决不同显示环境下的定位难题,提升工业自动化界面的交互体验。

weixin_29041767的博客 343

【多线程-初阶】Thread 类及常见方法

本节⽬标• 认识多线程• 掌握多线程程序的编写• 掌握多线程的状态• 掌握什么是线程不安全及解决思路• 掌握 synchronized、volatile 关键字。

2402_85428625的博客 2509

手把手教你用Qt和QScada框架,从零搭建一个简易的工业组态监控界面

本文详细介绍了如何使用Qt和QScada框架从零搭建工业组态监控界面,涵盖环境配置、界面设计、动态数据绑定及高级功能实现。通过实战案例演示,帮助开发者快速掌握SCADA系统开发技巧,提升工业自动化项目的开发效率。

weixin_32932149的博客 340

AI 模型优化与部署秘籍:从理论到实战

本文系统介绍了AI模型优化与部署的关键技术。模型剪枝通过去除冗余参数减小模型规模,量化技术降低计算精度以提升效率,蒸馏方法实现知识从大模型到小模型的迁移。TensorRT通过层融合和精度校准加速GPU推理,ONNX格式实现跨框架模型转换,TFLite则优化移动端部署。这些技术共同解决了AI模型在资源受限环境下的部署难题,显著提升了模型运行效率,为AI应用落地提供了重要支撑。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 247

【多线程】Thread 类及常见方法

1.Thread 类 在Java标准库中,提供有一个Thread,来表示/操作线程。 Thread 类也可以视为Java 标准库提供的API。 创建好的 Thread 实例,其实和操作系统中的线程是一一对应的关系。 操作系统,提供了一组关于线程API(C语言风格),Java对于这组API进一步封装了,就成了Thread类 2.创建线程 2.1继承Thread 类 通过Thread 类创建线程,最基本的做法就是创建子类,继承自Thread ,并且重写run方法 (1)继承Thread 来创建一

m0_60494863的博客 6290

Claude Code 从入门到精通,通过实战项目穿插 Agent Skills、MCP、Hooks、子代理、插件等十五个知识点

本文介绍了Claude Code的高级使用技巧,通过一个个人简历网站实战项目,详细讲解了15个核心知识点。主要内容包括:三种权限模式(默认/编辑/计划)的使用与切换;历史会话管理(/resume、/rename、/clear命令);会话回滚功能(/rewind);以及危险参数--dangerously-skip-permissions的注意事项。文章还演示了如何通过计划模式拆分代码文件,并强调了在复杂任务中仔细检查规划步骤的重要性。这些技巧能帮助开发者更高效地使用Claude Code完成项目开发。

1791

浅谈Thread类及常见方法与线程的状态(多线程编程篇2)

如何简单的去使用jconsloe 查看线程 (多线程编程篇1)_如何查看weblogic当前线程-优快云博客距离笔者发布本系列第一篇博客已过去一月有余,本篇博客,笔者将继续介绍已经整理学习好的内容既帮助未来的自己复习,也为正在阅读的你提供参考和思考.本篇博客的内容正如标题所示,笔者主要将介绍一下内容1.Thread类,并通过举例演示常见方法2.向大家介绍线程的状态.愿我们一起进步!

cjejwe的博客 2064

数学建模模型总结

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

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

多线程——Thread 类及常见方法

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。线程是程序中执行的最小单元,Thread类提供了创建和控制线程的各种方法。• 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证。转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。

YumunaoDAII的博客 1480

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

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

多线程-Thread类及常见方法

Thread类是Java编程语言中的一个重要类,用于支持多线程编程。它是java.lang包中的一部分,提供了创建和管理线程的方法和功能。通过使用Thread类,可以同时执行多个任务,实现并行处理。每个线程都有独立的执行路径,可以在不同的时间段内独立运行。这使得程序能够更高效地利用计算机的资源,并提高应用程序的响应速度。Thread类提供了许多方法来管理线程的行为,例如start()方法用于启动线程,join()方法用于等待线程的结束,sleep()方法用于暂停线程的执行等等。

weixin_71841320的博客 1544

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

JavaEE——多线程Thread 类及常见方法

文章进一步剖析多线程Thread类及其方法

weixin_72703349的博客 1127

Java多线程:常见的线程的创建方法及Thread类详解

线程是程序的执行流程的最小单元,一个进程(程序的执行实例)可以由一个或多个线程组成,每个线程都有自己的执行路径和执行状态。线程可以并发执行,即多个线程可以同时在不同的处理器核心或计算机上运行,从而提高程序的运行效率。线程与进程的区别在于,进程是操作系统对一个正在运行的程序的抽象,而线程是进程内部的一个执行单位。一个进程可以有多个线程,这些线程共享进程的资源,如内存空间、文件描述符等。线程之间可以通过共享内存的方式进行通信,相比于进程间通信(如管道、消息队列)的开销更小。

luming的博客 1653

【Java多线程】【Thread类及常见方法】

Thread类是 JVM用来管理线程的一个类,每个线程都有一个唯一的相关联的Thread对象;每个执行流需要一个对象描述,就是Thread类的对象,JVM讲这些Thread对象组织起来,用于线程调度,线程管理;

OTO233的博客 1057

多线程二 Thread 类及常见方法

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。 Thread类的对象就是用来描述 一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。 1 .Thread 的常见构造方法 方法 说明 Thread() 创建线程对象 Thread(Runnable target) 使用 Ru...

ITdong的博客 418

多线程|Thread类及常见方法

多线程中Thread类的介绍,构造方法的使用,以及Thread类的一些核心属性 还包括线程的启动start方法、线程中断和线程的等待join方法等的介绍

m0_61925333的博客 293

【JavaEE多线程】Thread类及其常见方法(上)

这篇文章我来为大家详细地介绍一下Thread类的常见的方法,以及线程的一些相关操作,当中包括如何中止线程、等待线程。

weixin_59658448的博客 993

(javaEE)多线程基础1: 进程和线程的联系和区别 。Java如何进行多线程编程?Thread 类及常见方法。

进程   1.进程具有独立性    ———— 虚拟地址空间 线程   为什么要引入多个线程?   多线程注意点   ⁜⁜总结:线程和进程的区别和联系⁜⁜ (经典面试题) Java如何进行多线程编程?   创建线程    ——方法1 继承 Thread 类    ——方法2 实现 Runnable 接口    —— 使用Runnable的写法 和 直接继承 Thread 有什么区别?    匿名内部类 方法     继承 Thread,重写run,但是使用匿

iiiiiihuang的博客 575

多线程(三)Thread 类及常见方法

一,Thread类的几个属性 二,启动一个线程-start() 三,中断线程的方法 四,等待一个线程-join() 五,休眠当前线程 六,获取当前线程的引用、 ...

淡沫初夏zz的博客 1881

多线程(二):Thread 类及常见方法

多线程(二):相似介绍了 Thread 类及其常用方法

2401_88599502的博客 1102

多线程(2)——Thread类及常见方法

本文介绍了Java中线程的构造方法、常见属性、启动、中断和等待操作。通过Thread(String name)可以为线程命名,便于调试。常见属性包括线程ID、名称、状态、优先级等。线程分为前台线程和后台线程,前台线程会阻止进程结束,而后台线程则不会。启动线程使用start()方法,中断线程可以通过变量控制或使用isInterrupted()方法。等待线程使用join()方法,可以确保线程按顺序执行。这些操作帮助开发者更好地管理和控制多线程程序的执行。

suger__salt的博客 722
上一篇: 多线程——初步认识线程
下一篇: 多线程——synchronized关键字
comerun
博客等级 码龄3年 242粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

comerun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值