多线程的基本操作

目录

创建线程

方法1 继承Thread类,重写run()

方法2 实现Runnable接口

方法三 使用匿名内部类来创建线程

方法4 使用匿名内部类实现Runnable

方法5 使用Lambda表达式

Thread类的常见用法

1.Thread的常见构造方法

2.Thread的常见方法

3. 线程中断

4. 线程等待

5. 获取当前线程

6. 休眠线程 


创建线程

每个线程都是一个独立的执行流,  多个线程之间是 " 并发 " 执行的.其中多线程最核心的地方就是抢占式执行,随机调度.接下来用Thread类来创建一个线程

方法1 继承Thread类,重写run()

class MyThread extends Thread {
    @Override
    public void run() {
        while(true) {
            System.out.println("hellow thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start(); //创建一个线程

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

在java标准库中提供了Thread类来表示一个线程,通过new MyThread来引用子类对象,在这个过程并没有创建线程,而真正创建进程的是通过thread.start() 方法来创建一个线程,在上述代码中包含两个线程

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

2. 通过start( )创建了一个新的线程

代码运行结果如下:

多线程的基本操作_第1张图片

上面代码看似是死循环,通过结果会观察到是在一直交替打印hellow Thread 和hellow main,就解决了并发执行的问题,操作系统调度线程的时候,是抢占式执行,具体那个线程先执行,那个后执行是不确定的,取决于操作系统调度器,虽然有优先级,但从应用程序上看看到的效果,就好像线程之间的调度顺序是随机的。

start()和run()的区别

●start()是真正创建了一个线程,一个线程是一个独立的执行流

●run()只是描述了线程的具体操作,如果直接在main中调用run(),此时没有创建线程,只有一个main线程。

线程中的sleep()方法

静态方法: Thread.sleep(1000);

单位: ms 

作用: 让当前线程进入休眠,进入“阻塞”状态,放弃占有CPU时间片,让给其他线程使用

方法2 实现Runnable接口

Runnable的作用是描述一个要执行的任务,重写的run()方法就是任务的执行细节

class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("hellow thread");
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        //描述一个任务
        Runnable runnable = new MyRunnable();
        //把任务交给线程来执行
        Thread t = new Thread(runnable);
        t.start();
    }
}

用Runnable接口来创建线程,是为了解耦合,让线程和线程要做的操作分开

方法三 使用匿名内部类来创建线程

多线程的基本操作_第2张图片

方法4 使用匿名内部类实现Runnable

多线程的基本操作_第3张图片

这个方法本质上和方法二相同,只不过把实现Runnable的任务交给匿名内部类的语法.此处是创建了一个类,实现Runnable,同时创建类的实例,并且传给了Thread的构造方法.

方法5 使用Lambda表达式

多线程的基本操作_第4张图片

把任务用lambda表达式来描述,直接把lambda传给Thread构造方法。

Thread类的常见用法

1.Thread的常见构造方法

多线程的基本操作_第5张图片

带两个参数的构造方法

多线程的基本操作_第6张图片

 使用匿名内部类的方法创建一个线程并且给这个线程起了个名字。

2.Thread的常见方法

下面是thread的几个常见用法

多线程的基本操作_第7张图片

3. 线程中断

下面中间讲解一些中断一个线程的方法

中断的意思不是让线程立即停止,而是线程A通知线程B你应该快要停止了,是否真的停止,取决于线程B这里具体的代码实现

使用标志位来控制线程是否中断

多线程的基本操作_第8张图片

在上面代码中设置flag为标志位,当flag为false的时候,导致循环结束,进而导致这个线程结束 

多线程的基本操作_第9张图片

上面进程结束完全取决于我们线程内部的代码.

使用Thread自带的标志位来控制线程是否中断

多线程的基本操作_第10张图片

1. Thread.currentThread()是Thread的静态方法,通过这个方法可以获取到当前线程,那个线程调用这个方法,就得到那个线程的对象的引用.

2. isInterrupted()表示线程是否被中断,为true表示被中断,为false表示未被中断.

3. 当代码执行到t.interrupt的时候,就可以把isInterrupted里的标志位进行设置为true

代码运行结果如下:

多线程的基本操作_第11张图片

4. 如果线程在sleep中休眠,此时调用interrupt就会把t线程唤醒,interrupt会触发sleep内部的异常,导致sleep提前返回.在sleep被唤醒的时候,又会把isInterrupted()设置为false,这就导致了sleep的异常被catch完了之后代码还会继续执行.

5.要想终止线程,我们可以在catch里面加一个break,可以跳出这个循环,中断这个线程

4. 线程等待

线程是一个随机调度的过程,等待线程做的事情就是控制线程的结束顺序.

多线程的基本操作_第12张图片

代码运行结果

多线程的基本操作_第13张图片

当执行完t.start之后,t线程和main线程并发执行,当执行到t.join的时候,main线程发生阻塞,一直阻塞到t线程执行结束,main线程才会从join中恢复过来,继续执行.

当在join之前,t线程已经执行结束,则main线程不会阻塞,就会继续执行.

join有三种方法

多线程的基本操作_第14张图片

5. 获取当前线程

使用静态方法currentThread()来获取当前线程,在那个线程中调用,就能获取到那个线程的实例

多线程的基本操作_第15张图片

在这段代码中获取到的就是我们新创建的线程

6. 休眠线程 

休眠线程,本质上就是让这个线程不参与调度了,一旦线程进入阻塞状态,对应的PCB就进入阻塞状态,当调用sleep(1000)对应的线程PCB就进入阻塞队列等待1000ms当这个线程等待结束回到就绪队列,虽然sleep(1000),但实际上考虑到调度的开销,对应的线程无法再唤醒之后立刻执行,实际上间隔的时间大概率要大于等于1000ms

sleep的用法

多线程的基本操作_第16张图片

你可能感兴趣的:(java,开发语言)