当前位置: 首页 > article >正文

javaEE初阶————多线程初阶(1)

多线程初阶————

1,认识线程

1.1 概念

1)线程是什么

线程就是一个“执行流”,可以理解为程序执行的最小单位;

可以看成轻量级的进程;

2)为啥要有线程

“并发编程” 的需要,但是我们不是已经有进程了吗,我们要知道,我们进行的是服务器开发,我们在访问网站的时候,一个用户进行访问就是一个进程,用户的数据量是非常庞大的,进程的创建和销毁需要的开销就会变得非常非常大,这样我们就引出了线程,让一个进程中包含一个或多个进程,提升效率,

3)线程和进程的区别

1,进程是操作系统进行资源分配的基本单位,线程是操作系统进行运算调度的基本单位;

2,线程的创建,销毁,调度需要的开销更小;

3,进程之间互不影响,同一进程下的线程会互相影响,创建进程后会自动创建一个线程,第一个线程会涉及到申请资源的操作,其余线程不会涉及,进程销毁才会释放资源,线程的销毁不会释放资源;

4,因为线程是调度相关,所以每一份线程都有调度相关的数据

5,一个进程死掉了不会影响其他进程,但是一个进程中一个线程死掉了就掀桌了,全部都运行不了了;

4)java中线程和操作系统的关系

java中包装好了操作系统中对线程操作的API,但是java是不推荐多进程编程的,我们只去学习多线程编程;

1.2 第一个多线程程序

class MyThread extends Thread{
    public void run(){
        System.out.println("myThread");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();

        System.out.println("main");
    }
}

 main就是进程刚创建我们自动生成的第一个线程,运行

只是个示范,看接下来的讲解就好;

1.3 创建线程

1)创建对象继承Thread类
class MyThread2 extends Thread{
    public void run(){
        while(true){
            System.out.println("MyThread2 线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new MyThread2();
        
        System.out.println("主线程");
    }
}

我们创建一个类,让它继承Thread类,Thread类中给我们提供了一个run方法,让我们自己去写里面的内容,我们就在自己实现的类中重写run方法,我们循环打印,并且打印一次睡眠1秒,在主线程也就是main方法中,打印主线程,我们来看运行结果;

 

只有一个主线程,因为我们没有去调度线程,我们可以直接用.run或者是.start来开启线程

thread.run();

程序一直在运行,这里不明显,我们来借助一个工具,

找到jdk中的bin ,

以管理员身份运行它

找到我们刚才创建的Demo2

点击线程

我们看到main线程一直在等待,因为我们使用的run方法,所以是在主线程上运行的,如果我们想看到我们自己创建的线程,就要用start

我们在试试;

这个Thread——0就是我们自己创建的线程,但是main呢,还有为啥先打印的主线程呢,因为调度随机的,我们不知道操作系统让拿个线程先执行,所以就会发生这样的状况,这也是我们后期要重点掌握的,要怎么保证线程之间协调配合,避免乌鸦哥掀桌,哈哈哈,main线程在这里已经结束了,没啥好说的了,下一个; 

2)实现Runnable接口
class MyRunnable implements Runnable{
    public void run(){
        while(true){
            System.out.println("MyRunnable 线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo3 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
        System.out.println("主线程");
    }
}

这是第二种,我们Runnable接口,我们还是要使用Thread类来创建,这不有病吗.........这么麻烦,还不如用第一种,其实这种想法是不对的,大家听,没听过,高内聚低耦合,这里就谈到了低耦合,我们使用接口,在想要修改的时候去修改接口的代码即可,是不影响Thread的,但是我们使用Thread的时候,想要修改的时候,就要修改Thread中的代码, 可能扯到线程相关的代码,而且用类继承一次局限性大,接口更灵活;

运行

我们这次让主线程也活着


class MyRunnable implements Runnable{
    public void run(){
        while(true){
            System.out.println("MyRunnable 线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

 来运行

这次更能看到随机调度的现象

3)匿名内部类(Thread)

这几个其实用的都少,最多用到的还是lambda表达式

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(){
            public void run(){
                while(true){
                    System.out.println("Thread 线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        
        
        thread.start();
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

跟之前都一样,就是使用·匿名内部类了;

直接看运行

4)匿名内部类(Runnable)
public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread =  new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("Thread 线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        thread.start();
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

一样嗷,匿名内部类创建Ruunable对象,

5)lambda表达式

这个才是我们使用最多的方法,主要是很方便;

lambda表达式:

(参数)->{实现了啥}

public class Demo6 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
           while (true){
               System.out.println("Thread 线程");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        
        thread.start();
        
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

 方便吧,在new Thread的时候直接在括号中使用lambda表达式就行;

大家可能有疑问,不说重写run方法吗,这个{}里面的就是我们已经重写了,这个跟那个第三个匿名内部类的方法其实很像的;

我们来看运行结果;

完美嗷

2,Thread类及常见方法

Thread类是JVM用来管理线程的一个类,我们每创建一个Thread对象就有一个线程与他对应;

2.1 Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象并命名
Thread(Runnable target,String name)使用Runnable

 Thread thread1 = new Thread();
        Thread thread2 = new Thread(new Runnable() {public void run() {}});
        Thread thread3 = new Thread(()->{
            while (true){
                System.out.println("线程3");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"线程3");
        
        Thread thread4 = new Thread(new Runnable() {public void run() {}},"线程4");

我们用4种构造方法创建了线程,我们来观察一下线程3,我们是否把线程的名字修改了呢;

 

 成功看到线程3了;

2.2 Thread的常见属性

属性获取方法
ID

getId()

名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrputed()

1) ID 类似进程的pid,线程的唯一标识,不同线程不会重复;

2) 名称 各种调试工具用到;

3) 线程当前所处的情况;

3) 通常来说优先级高的线程会容易调用;

4) 可以想象为饭局中的小程序员,对这次饭局不起决定性作用,JVM会在一个进程的所有非后台线程结束后结束;

我们可以使用SetDaemon()来把当前线程设置为后台线程;

5) run方法是否结束;

6) 终止线程运行;

我们来写一个代码获取所以线程信息;

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "还活着");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "即将死亡");
        },"线程1");


        System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());
        System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());
        System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());
        System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());
        System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());
        System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());

        thread.start();
        while (thread.isAlive()){
            System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());
            System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());
            System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());
            System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());
            System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());
            System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());

            System.out.println(Thread.currentThread().getState() + " " + thread.getState());
            Thread.sleep(1000);
        }
        System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());
        System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());
        System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());
        System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());
        System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());
        System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());

    }
}

 运行之后就能看到整个过程了;

2.3 启动一个线程

我们之前用过run方法来启动线程,实际上着并不是真正创建了线程,我们使用start真正在操作系统底层创建了一个线程,只有创建了线程对象再start才是让线程真正独立执行了;

2.4 中断一个线程

线程一旦工作就会等到任务结束才会停下来,但是有时候我们有让线程立即停下的需求,我们有两种办法来中断一个线程,其实叫终止更好,因为不是间断,而是线程就结束了;

我们来模拟一个场景,有两个员工张三,李四,老板让他们去给别人转账,张三正在转给骗子,李四及时阻止;

1,共享标记来中断线程

public class Demo3 {
    public static boolean a = true;
    public static void main(String[] args) {
        Thread thread1 = new Thread(()->{
            while (a){
                System.out.println(Thread.currentThread().getName() + "正忙着转账呢");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "我嘞个豆,差点转走了");
        },"张三");

        Thread thread2 = new Thread(()->{
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "老板来电话了!" + "张三在给骗子转账!");
                a  = false;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"李四");

        thread1.start();
        thread2.start();
    }
}

来看运行结果 

哈哈哈哈哈,好玩吧; 

2,调用interrupt()方法来通知

方法说明
Thread对象.interrupt()中断对象关联的线程,如果线程正在阻塞,以异常方式通知,否则设置标志位
public static boolean interrputed();判读当前线程的标志位是否设置,调用后清除标志位;
public boolean 判读当前线程的标志位是否设置,调用后不清除标志位;

在Java线程的上下文中,中断标志位是Thread对象维护内部的布尔值用于表示该线程是否被请求中断。 

public class Demo3 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(()->{
            //或者用Thread.interrupted();
            while (!Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName() + "正忙着转账呢");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "我嘞个豆,差点转走了");
        },"张三");

        Thread thread2 = new Thread(()->{
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "老板来电话了!" + "张三在给骗子转账!");
                thread1.interrupt();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"李四");

        thread1.start();
        thread2.start();
    }
}

这里的原理就一样了,但是代码运行会报一个异常,

这个是因为

thread1.interrupt();

 唤醒了sleep让他直接抛出InterruptedException,被捕获到,抛出RuntimeException异常,所以我们在这里直接break就行;

这样结果就对了;

2.5 等待一个线程

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等待millis毫秒
public void join(long millis, int nanos)等待线程结束,精度更高;后面是纳秒;

 

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"张三");

        Thread thread2 = new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"李四");

        thread1.start();
        thread1.join();

        thread2.start();
        thread2.join();

        System.out.println("全部线程打印结束");
    }
}

我们创建了两个线程和主线程,thread1.join意思为让主线程等待thread1线程执行完再执行 ,此时thread2还没开启,我们来看运行结果

 

 

张三始终在工作我天,因为我们使用的join没有放参数,是无休止的等待;如果我们放参数就不会这样傻傻的等待了, 

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        },"张三");

        Thread thread2 = new Thread(()->{
            while (!Thread.interrupted()){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        },"李四");

        thread1.start();
        thread1.join(2000);
        thread1.interrupt();

        thread2.start();
        thread2.join(2000);
        thread2.interrupt();

        System.out.println("全部线程打印结束");
    }
}
");

修改一下代码。。。。。 

 

我们这回看到第一个join先让thread1插队,thread1运行2毫秒后,主线程启动,设置中断标志位,thread1停止,thread2插队,等待两毫秒后嗝屁,主线程也结束了; 

2.6 获取当前线程的引用

 这个之前我们就使用过了;

方法说明
public static Thread currentThread() 返回当前对象的引用,类似this

没啥好说的嗷,来段代码就好了;

public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            System.out.println(Thread.currentThread().getName());
        },"线程1");

        thread.start();
        System.out.println(Thread.currentThread().getName());

    }
}

 运行结果

2.7 休眠当前线程

这个也没啥好说的,我们一直在使用

方法说明
public static void sleep (long millis) throws InterputedException 休眠当前线程millis毫秒
public static void sleep (long millis,int nanos) throws InterputedException 更高精度

不演示了嗷,马上下一期


http://www.kler.cn/a/488363.html

相关文章:

  • OpenPCDet从环境配置到模型训练
  • 获得PostgreSQL中级认证后,可以从事哪些工作岗位?
  • MySQL数据导出导入
  • 计算机网络之---数据链路层的功能与作用
  • GPU算力平台|在GPU算力平台部署Qwen-2通义千问大模型的教程
  • 功能篇:mybatis中实现缓存
  • Git 常用命令指南
  • Vue.js 组件开发指南
  • 模式识别-Ch3-极大似然估计
  • Euler 21.10(华为欧拉)安装oracle19c-RAC
  • Django SimpleUI 配置详解:SIMPLEUI_CONFIG 和常用图标
  • Windows系统安装ComfyUI
  • 简单易用的PDF工具箱
  • 编译时找不到需要的库,如何在PyCharm中为你的项目添加需要的库
  • OpenPCDet从环境配置到模型训练
  • CAN201 Introduction to Networking(计算机网络)Pt.5 网络安全
  • 【Web安全】SQL 注入攻击技巧详解:UNION 注入(UNION SQL Injection)
  • 2025最新版Visual Studio Code安装使用指南
  • 学习反射5(获取成员方法)
  • 中学综合素质笔记3
  • 创建基本的 Electron 应用项目的详细步骤
  • 学习及笔记
  • 动力节点crm项目业务分析笔记
  • 性能测试05|JMeter:分布式、报告、并发数计算、性能监控
  • WMS仓库管理系统,Vue前端开发,Java后端技术源码(源码学习)
  • 飞书机器人告警实现