多线程基础知识点梳理

  • 创建线程的3种方式:

1.继承:extends Thread类 继承的子类需要重写run方法

public class CreatThread1 extends Thread{

    public void run() {

        System.out.println("线程CreatThread1执行");

    }
}

执行线程方法:

        //加载启动线程CreatThread1
        CreatThread1 creatThread1=new CreatThread1();
        creatThread1.start();

2.实现:implements Runnable接口

public class CreatThread2 implements Runnable {

    @Override
    public void run() {

        System.out.println("线程CreatThread2执行");

    }
}

执行线程方法:

        //加载启动线程CreatThread2
        CreatThread2 creatThread2=new CreatThread2();
        Thread thread2=new Thread(creatThread2);
        thread2.start();

3.实现 Callable 接口

public class CreatThread3 implements Callable<Integer> {
    @Override
    public Integer call()  throws Exception {
        
        System.out.println("执行线程CreatThread3");
        return 1;
        
    }
}

执行线程方法:

        //加载启动线程CreatThread3,并获取返回值
        CreatThread3 creatThread3=new CreatThread3();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(creatThread3);
        Thread thread3=new Thread(futureTask);
        thread3.start();
        Thread.sleep(1000);
        Integer integer = futureTask.get();
        System.out.println(integer);
        System.out.println("主线程");

Callable 注意:
1.获取返回值使用futureTask.get()方法
2.测试后发现,主线程调用futureTask.get()方法后会进入阻塞,在线程执行完毕,返回值后,才会获取到值,解除阻塞,且如果在主线程执行到futureTask.get()方法之前,Callable 线程已经执行结束,则futureTask.get()方法会直接获取到值,并继续执行

Callable创建线程方式与Runnable方式创建线程的区别:
1.Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值
2.Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常。

  • 关于线程各个状态:

下方以第一种创建线程的方式举例

  1. 新建状态(New)
    Thread thread1=new Thread(creatThread1);//新建状态
    此时它仅仅由 JVM 分配了内存,并初始化其成员变量值,它会一直保持这个状态直到调用该对象的 start 方法。
  2. 就绪状态(Runnable)
    thread1.start();//就绪状态
    就绪状态的线程会放在一个就绪队列中,等待 JVM 里的调度器进行调度。处于就绪状态的线程,随时可能被 CPU 调度执行。
  3. 运行状态(Running)
    就绪状态的执行被 CPU 调度执行,就可以执行 run方法,此时线程就处于线程状态。处于运行状态的线程最复杂,它可以变为阻塞状态就绪状态死亡状态。需要注意一点,线程变为运行状态之前的状态只能是就绪状态。
  4. 阻塞状态(Blocked)
    线程变为阻塞状态是因为某种原因放弃 CPU 的使用权,暂时停止运行,如果执行了 sleep、suspend 等方法,释放了所占用的资源之后,线程就从运行状态进入阻塞状态。等待睡眠时间结束或者获得设备资源之可以重新进入就绪状态。阻塞可以分为以下三种:
    1.等待阻塞 处于运行状态的线程调用wait方法,会使线程进入等待阻塞状态
    2.同步阻塞 当线程获取 synchronized 同步锁因为同步锁被其他线程占用而失败后,会使线程进入同步阻塞
    3.其它阻塞 通过调用线程的sleep或join发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep状态超时,join等待线程终止或超时,或者 I/O 处理完毕,线程重新回到就绪状态。
    注意:sleep不释放锁,wait释放锁
  5. 死亡状态(Dead)
    一个处于运行状态的线程执行完了 run 方法或者因为其它终止条件发生时,线程就会进入到死亡状态,该线程结束生命周期

线程状态图

状态图.png
  • 线程中常用方法

  • 以下继承自:Object
    public final native void notify()
    唤醒在此对象监视器上等待的单个线程,使其进入就绪状态

public final native void notifyAll()
唤醒在此对象监视器上等待的所有线程,使其进入就绪状态

public final void wait()
让当前线程处于·等待阻塞状态,直到其他线程调用此对象的notify方法或notifyAll方法,当前线程被唤醒,会释放它所持有的锁

public final native void wait(long timeout)
让当前线程处于·等待阻塞状态,直到其他线程调用此对象的notify方法或notifyAll方法,当前线程被唤醒

public final void wait(long timeout, int nanos)
让当前线程处于·等待阻塞状态,直到其他线程调用此对象的notify方法或notifyAll方法或者其他某个线程中断当前线程,或者已超过某个实际时间量,当前线程被唤醒

  • 以下继承自Thread

public static native void yield()
暂停当前正在执行的线程对象,并执行其他线程,yield 方法不会释放锁

public static native void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),sleep 方法不会释放锁

public final void join()
当某个程序执行流中调用其他线程的 join 方法时,调用线程将被阻塞,直到被 join 的线程执行完毕

public void isAlive()
确认线程是否存活

public void interrupt()
用于中断本线程,这个方法被调用时,会立即将线程的中断标志设置为 true

public static boolean interrupted()
Thread 类的一个静态方法,它返回一个布尔类型指明当前线程是否已经被中断,interrupted 方法除了返回中断标记之外,它还会清除中断标记(即将中断标记设为 false)

public boolean isInterrupted()
Thread 类的一个实例方法,它返回一个布尔类型指明当前线程是否已经被中断,isInterrupted 方法仅仅返回中断标记,不会清楚终端标记

多线程常用方法详解

  • wait 方法与 notify 方法

在 Object 类中定义了 wait 方法和notify 方法,wait 方法的作用是让当前线程进入等待状态,将当前线程置入 预执行队列,会在wait 方法所在代码处停止执行,直到被通知或者被中断,在调用 wait方法之前,线程必须获取该对象的锁,因此只能在同步方法或者同步代码块中调用 wait方法,并且该方法会释放当前线程锁持有的锁。notify方法是唤醒在当前对象上等待的单个线程,如果有多个线程等待,那么线程调度器会挑出一个 wait 的线程,对其发出notify,并使它等待获取该对象的对象锁,这意味着,即使收到了通知,线程也不会立即获取到对象锁,必须等待notify方法的线程释放锁才可以。和 wait 方法一样,notify 方法也只能在同步方法或者同步代码块中调用。它还有个相似的方法notifyAll,它的作用是唤醒在当前对象上等待的所有线程。

  • sleep 方法

waitnotify方法不同,sleep 方法定义在 Thread 类中,从方法名也可以知道,这个方法的作用就是让当前线程休眠,即调用该方法后当前线程会从运行状态(Running)进入到阻塞(休眠)状态(Blocked),同时该方法必须指定休眠的时间,当前线程的休眠时间会大于或者等于这个指定的休眠时间。当线程重新被唤醒时,线程会由阻塞状态(Blocked)变成就绪状态(Runnable),然后等待 CPU 的调度执行。

注意:sleep不释放锁,wait释放锁
示例程序:
Thread1.java文件

public class Thread1 implements Runnable {
    public void run() {

        System.out.println("进入");
            synchronized (this){
                System.out.println("等待");
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("解除成功");

            }

    }
}

main函数

    public static void main(String[] args) throws InterruptedException {

        Thread1 thread1=new Thread1();
        Thread thread=new Thread(thread1);
        System.out.println("就绪");
        thread.start();
        Thread.sleep(1000);
               synchronized (thread1){
                System.out.println("解除");
                thread1.notify();
                System.out.println("..............");

            }
            
    }
  • yield 方法

yield方法定义在Thread 类中,是线程特有的方法。此方法的主要作用是让步,它会使当前线程从运行状态(Running)变为就绪状态(Runnable),从而让其他具有同样优先级的处于就绪状态的线程获取到 CPU 执行权(PS: CPU 会从众多的处于就绪状态的线程里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到),但是,也并不能保证在当前线程调用 yield 之后,其它哪些具有相同优先级的线程就一定能获得执行权,也有可能是当前线程又进入到运行状态(Running)继续运行。
注意:yield 不释放锁

  • join 方法

在有些场景中我们需要在子线程去执行一些耗时的任务,但是我们的主线程又必须等待子线程执行完毕之后才能结束,那么此时就可以使用 join 方法了,该方法定义在 Thread 类中,方法的作用是:让主线程等待子线程执行结束之后才能继续执行
例:

//加载启动线程CreatThread1
        CreatThread1 creatThread1=new CreatThread1();//新建状态
        creatThread1.start();//就绪状态
        creatThread1.join();
        System.out.println("主线程");

结果图

参考文章:https://juejin.im/post/6844904020943831053

Thread类中interrupt()、interrupted()和isInterrupted()方法

  • 关于这三个方法:

interrupt()是给线程设置中断标志,但是线程实际还是在运行;interrupted()是检测中断并清除中断状态;isInterrupted()只检测中断。还有重要的一点就是interrupted()作用于当前运行线程,interrupt()和isInterrupted()作用于调用方法的线程
例:

        Thread2 thread2=new Thread2();
        Thread thread=new Thread(thread2);
        thread.interrupt();//作用于thread线程
        thread.isInterrupted();//作用于thread线程
        Thread.interrupted();//作用于当前线程

如果想要调用interrupt()线程中断运行,一般可以这样:

    public void run() {
       while (!thread.isInterrupted()){
           
       }
    }
  • 作用:

我们通过“中断”方式终止处于“阻塞状态”的线程。当线程由于被调用了sleep(),wait(),join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生InterruptedException异常。将InterruptedException放在适当的为止就能终止线程

public void run() {
        try {
           while (!thread.isInterrupted()){

                   Thread.sleep(2000);

           }
       } catch (InterruptedException e) {
            System.out.println("异常");
            e.printStackTrace();
        }
    }
        thread.interrupt();
        System.out.println(thread.isInterrupted());
        System.out.println("结束");
结果图

参考文章:
https://blog.csdn.net/qq_39682377/article/details/81449451
https://blog.csdn.net/wanliguodu/article/details/81071562

  • 线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。Java 线程的优先级是一个整数,其取值范围是1~ 10。默认情况下,每一个线程都会分配一个优先级NORM_PRIORITY(5)。具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源,Thread 类提供了 setPriority 和 getPriority 方法来更改和获取线程优先级(需要注意的是: 线程优先级不能保证线程执行的顺序,而且非常依赖于平台)。

参考文章:https://juejin.im/post/6844904015906471950

关于synchronized的基本规则

锁对象lock是指对象所指向的内存地址
第一条:当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程对该对象的该synchronized方法或者synchronized代码块的访问将被阻塞。
第二条:当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程仍然可以访问该对象的非同步代码块。
第三条:当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程对该对象的其他的synchronized方法或者synchronized代码块的访问将被阻塞。

关于实例锁全局锁有个很好的例子。

pulbic class Something {
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}

假设,类Something有两个实例(对象)分别为x和y。分析下面4组表达式获取锁的情况。

(01) x.isSyncA()与x.isSyncB()//不能同时访问两个都是用x当做锁
(02) x.isSyncA()与y.isSyncA()//可以同时访问,因为两个锁不同
(03) x.cSyncA()与y.cSyncB()//不可以被同时访问,他们使用的都是Something作为锁
(04) x.isSyncA()与Something.cSyncA()//可以被同时访问

参考文章:
https://blog.csdn.net/wanliguodu/article/details/81005560

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容