【多线程】-多线程的创建和使用

本片学习多线程的几种创建方式


线程的创建

1、继承 Thread 来创建一个线程类

Java标准库提供了一个类Thread能表示一个线程

实现步骤

  1. 继承 Thread 来创建一个线程类.重写run方法

  2. 创建 MyThread 类的实例

  3. 调用 start 方法启动线程

class MyTherad extends Thread{
    @Override
    public void run() {
        System.out.println("hello");
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new Thread();//先创建MyThead实例,t的引用实际指向子类的实例
        t.start();//启动线程,在进程中搞了另一个流水线,新的流水线开始并发执行另提供逻辑
    }
}


在这里插入图片描述

上述代码涉及到了两个线程

  1. main方法对应的线程(一个进程至少得有一个线程)也可称为主线程

  2. 通过t.start创建的新线程

在这里插入图片描述
在这里插入图片描述
使用Tread的run描述线程入口

t.start会创建一个新的线程并执行
t.run()会执行里面的线程

run不是一个随便的方法,重写了父类的方法,
这种重写一般称为功能的扩展,不需要手动调用

2、实现使用 Runnable 接口

Runnable描述一个具体的任务
通过run方法重写
使用Runnable interface 来描述线程入口

实现步骤

  1. 实现 Runnable 接口
  2. 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.。
  3. 调用 start 方法
class MyRunable implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("hello");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        MyRunable runable = new MyRunable();
        Thread t = new Thread(runable);
        t.start();
        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

使用Runnable interface 来描述线程入口

继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.

实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()

3、匿名内部类创建 Thread 子类对象

public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunable(){
            @Override
            public void run() {
                while (true){
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        t.start();

        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4、匿名内部类创建 Runnable 子类对象

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t =new Thread(){
            @Override
            public void run() {
                while (true){
                    System.out.println("hello t");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t.start();

        while (true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

5、lambda 表达式创建 Runnable 子类对象

语法规则:

public static void main(String[] args) {
        Thread t = new Thread(() ->{
        });
    }

()里面放参数,如果只有一个参数,()可以省略,{}里面放函数体、代码

Thread类中的方法

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
java代码中的Thread对象和操作系统中的线程是一一对应的。
在这里插入图片描述

在这里插入图片描述

  1. ID :是线程的唯一标识,不同线程不会重复
  2. name:名称是各种调试工具用到
  3. state: 状态表示线程当前所处的一个情况,下面我们会进一步说明
  4. priority :优先级高的线程理论上来说更容易被调度到
  5. daemon :关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
  6. alive: 是否存活,即简单的理解,为 run 方法是否运行结束了
  7. 线程的中断问题

true 表示后台线程
false表示前台线程
前台线程会组织Java线程结束
必须得java线程中所以的前台线程都执行完,Java线程才能结束
创建的线程默认是前台的,可以通过setDeamon设置为后台的

6.实现Callable

类似Runnable :描述了一个任务,一个线程干什么,通过run方法返回,返回值类型void

callable 方法有返回值

public class ThreadDemo27 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //这只是创建一个任务
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {//这个泛型类型是什么,call方法的返回值类型就是什么
                int sum = 0;
                for (int i = 0; i <= 10050; i++) {
                    sum += i;
                }
                return sum;
            }
        };

        //Thrad不能直接传callable,需要包装一层
        FutureTask<Integer> futureTask = new FutureTask<>(callable);// FutureTask未来要执行的任务
        Thread t = new Thread(futureTask);
        t.start();

        System.out.println(futureTask.get());/*此处get就是获取上述任务call方法返回值的结果,call方法是t线程的,get方法是主线程的
        如何保证get的时候,t线程执行完了,get和join类似都会阻塞等待*/
    }
}

线程的使用

线程的启动

start 方法:真正从系统这里,创建一个线程,新的线程将会执行run方法

调用 start 方法, 才真的在操作系统的底层创建出一个线程.

run方法:表示线程的入口方法是什么(线程启动起来,要执行哪些逻辑)

线程的中断

线程的终止
本质上来说,让一个线程终止,办法只有一个,结束该线程的入口方法执行完毕(return,抛出异常)
方法:
1.给线程设定一个结束标志位

public class ThreadDemo9 {
    public static boolean isQuit = false;
    public static void main(String[] args) {
    public static boolean isQuit = false;//区别
        Thread t =new Thread(()->{
           while (!isQuit){
               System.out.println("hello t");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
            System.out.println("t,线程终止");
        });

        t.start();

        //在主线程中,修改isQit
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit = true;
    }
}

lambda 表达式能否访问外部变量
Java要求变量捕获,捕获的变量必须是final或者“实际的final”(变量没有被final修饰,但代码中没有做出修改)

package threading;

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            //currentThread是获取当前线程的实例
            //此处 currentThrerad得到的就是t
            //isInterruptead就是t对象里自带的一个标志位
           while (!Thread.currentThread().isInterrupted()){
               System.out.println("hello t");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
                   break;
               }
           }
        });

        t.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();//打印出当前异常位置的调用栈
        }
        //把t内部的标志位设置位true,,,默认的是false
        t.interrupt();//1.设置标志位为true,2.如果该线程正在阻塞(比如执行sleep,此时会把阻塞状态唤醒,通过抛异常的方式让sleep立即结束
        //当sleep被唤醒,sleep会自动把isInterrupted的标志位清空(true->false),,,目的就是为了让线程自身能够对线程何时结束,有一个明确的控制
    }
}

三种方法,1.无视 2.立即 3. 等待
interrupt方法效果,不是让线程立即结束,而是告诉你线程该结束了,只是通知而不是命令
搞个break

线程的等待

join方法
调用谁,等待他等待

阻塞//走到这停下,当前这个线程暂时不参与cpu的调度

1.main线程调用t.join的时候,如果t还在运行,此时main线程阻塞,直到t执行完毕,main才从阻塞中解除,才继续执行
2.main线程调用t.join的时候,如果t已经结束了,此时join不会阻塞,就会立刻往下执行难,保证join先执行

join两个版本:无参版本(死等) 有参版本,指定最大等待时间,如果等待时间达到了上限,就不等了

获取当前线程的引用

public static Thread currentThread();           返回当前线程对象的引用
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

休眠当前线程

使用Thread类的sleep()方法。
sleep()方法有两种重载形式:

  1. static void sleep(long millis):使当前线程休眠指定的毫秒数。
  2. static void sleep(long millis, int nanos):使当前线程休眠指定的毫秒数和纳秒数。
    代码示例
public class SleepThreadExample {
    public static void main(String[] args) {
        System.out.println("Main thread starts...");

        // 创建并启动新线程
        Thread childThread = new Thread(() -> {
            try {
                System.out.println("Child thread starts...");
                Thread.sleep(3000); // 休眠3秒
                System.out.println("Child thread resumes...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        childThread.start();

        // 主线程继续执行
        System.out.println("Main thread continues...");

        try {
            childThread.join(); // 等待子线程执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Main thread ends.");
    }
}

线程的状态

NEW: 安排了工作, 还未开始行动
RUNNABLE: 就绪状态/可工作的. 又可以分成正在CPU上运行和准备好随时可以去CPU上运行

  1. Ready:线程在就绪队列上等待调度
  2. Running: 线程在CPU上运行

BLOCKED: 这几个都表示排队等着其他事情 //表示等待锁出现的状态
WAITING: 这几个都表示排队等着其他事情 //表示wait方法数出现的状态
TIMED_WAITING: 这几个都表示排队等着其他事情//指定时间等待
TERMINATED: 工作完成了.

public class ThreadDemo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while (true){
                //为了防止hello把线程状态冲没了,先注释掉
                //System.out.println("hello");
            }
        });

        //在启动前,获取进程状态   NEW
        System.out.println(t.getState());

        t.start();


        //线程结束后的状态 TERMINATED
        Thread.sleep(2000);
        System.out.println(t.getState());

    }
}

状态转换图

一主线三分支
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值