当前位置:   article > 正文

Java多线程基本概念和常用API(面试高频)_java 线程api

java 线程api

提示:本文带大家入门JavaSE基础的多线程部分,更深层次建议看书籍《Java并发编程》
全文主要内容为:
1.多线程的生命周期原理
2.多线程的常用API:面试常问
3.并发和并行之间的联系
本人能力有限,如有遗漏或错误,敬请指正,谢谢


其他文章

1.Java多线程基本概念和常用API(面试高频)
2.线程安全问题(synchronized解决,各种类型全)
3.Java并发线程池使用和原理(通俗易懂版)
4.基于SpringBoot+Async注解整合多线程

前言

学习一门技术最好使用wwh方法
what:这门技术是什么
why:为什么用这个技术,使用会有什么优化
how:怎么使用


提示:以下是本篇文章正文内容,下面有代码案例可供参考

一、进程和线程关系

1.1 基本概念

不打算按照网上其他文章以概念的形式解释这个关系。

第一个例子:电脑上打开一个微信,就启动了一个进程(可以在任务管理器查看),所以进程就是一个运行的程序,那么在微信上你可以打开小程序或者朋友圈,那么你打开这两个是不冲突的,这两个也就是两个线程

第二个例子:进程就相当于一个公司,线程相当于公司的员工,一个公司里面有很多个员工,也就是说一个进程可以启动多个线程
在这里插入图片描述

1.2 二者内存关系

一个进程中线程A和线程B独立开辟一个栈内存,但是堆内存和方法区共享。一个线程一个栈

1.3并行和并发

引用其他博主的,这篇写的通俗易懂,大家可以移步学习后再回来

https://www.cnblogs.com/Hei-Tao-K/p/10142561.html

在这里插入图片描述

二、常用API:可以复制代码自己尝试结果

2.1 启动线程用start()方法

start()方法作用是:启动一个分支线程,在JVM中开辟新的栈空间,只要新的栈空间开辟出来后,start()方法就结束了。启动成功后会自动调用run()方法

run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码;程序中只有主线程这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

2.2 sleep()方法

作用:让当前线程进入休眠期
如果没有写在分线程run()方法中,而是主线程main中,那么是让主线程休眠,不是分线程
写在哪个线程里就让哪个线程睡眠

public class Test{
    public static void main(String[] args){
        MyThread my1=new MyThread();
        //分线程my1开启
        my1.start();
        try{
        //这里的sleep()方法没有放在run()中,所以作用在主线程中:睡眠10s
            Thread.sleep(10000);                  
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        //主线程main:由于上面主线程睡眠10s,所以分线程先会执行,10s后主线程加入进来
        for(int i=0;i<100;i++){
            System.out.println(i);
        }
    }
}
//静态内部类
public static class MyThread extends Thread{
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println(i);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

2.3 合理终止一个线程执行

原理:加一个flag标志位

public class Test{
    public static void main(String[] args){
        MyThread my1=new MyThread();//创建MyThread对象
        my1.start();//启动分线程
        try {
            Thread.sleep(5000);//主线程睡眠5秒
        } catch (InterruptedException e) {
            e.getMessage();
        }
        my1.flag=false;//线程睡眠5秒后将标志位变成false,退出循环,即线程中断
    }
}

public static class MyThread extends Thread{
    boolean flag=true;//标志位
    
    public void run(){
        for(int i=0;i<10;i++){
        //相当于让flag当作一个控制器,flag为true就是开启,false就是关闭
            if(flag){
                System.out.println(i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                return;
            }
        }
    }
}	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

2.4获取当前线程信息

//获取当前线程
Thread t1=Thread.currentThread();
//获取当前线程的名字
String name=t1.getName();
//设置当前线程的名字
t1.setName("设置线程名字");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.5线程调度相关方法

该知识点关键:优先级比较高的只是获取时间片的概率更大一点,并不是优先级高的一定获取时间片

常见的线程调度模型:

  • 抢占式调度模型

哪个线程优先级高,抢到的CPU时间片的概率就会高一些,JAVA采用的就是抢占式模型。

  • 均分式调度模型

平均分配CPU时间片,每个线程占有的CPU时间片时间长度一样,平均分配,一切平等,有一些编程语言采用这种方式

最低的优先级为1 最高的优先级为10 默认的优先级为5
void setPriority(int new Priority) 设置线程的优先级
int getPriority()获取线程的优先级
  • 1
  • 2
  • 3

2.6 线程让步

(谁调用这个方法就是谁让步)

Thread.yield();
  • 1

先来说说多个线程启动时,是谁先执行呢?
在Java中采取的是抢占式调度模型,举个例子,有一台电脑,三个人想玩,但这台电脑每次只能由一个人玩3分钟,所以这三个人就要抢到这个电脑位置,等三分钟后,三个人再重新抢位置。
这三个人也就是三个线程,电脑位置也就是CPU时间片,当某个线程抢夺到时间片,就能执行run()方法。

线程让步:以上面的例子来说,抢到位置的人突然不想玩了(ps:他只是想体验一下抢位置的感觉),所以这时候三个人又重新抢位置

static void yield() 暂停当前正在执行的线程

yield()方法不是阻塞方法,让当前线程让位, 给其他线程使用,yeld()方法的执行使得线程从“运行状态”变回“就绪状态”。
注意:yield()方法让执行该方法的线程放弃当前对CPU的执行权,并让所有线程开始抢夺CPU的执行权。此时每一条优先级最高的线程都有可能抢到CPU的执行权,包括刚刚放弃了执行权的线程!

2.7线程合并

void join() :中途加入其他线程,其他线程执行完了才轮到本线程,继续抢夺cpu
注意:线程必须要先启动了,才能中途加入,只有启动了,才能对线程进行操作。

举例子:小明正在看电视剧,看到一半,中途突然加入了广告,但是不能点掉,只能等广告结束后,再继续回到电视剧。可以理解为插队

public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("线程插队"+i);
        }
 
    }
 
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
 
        for (int i = 0; i < 200; i++) {
            //当主线程执行到100的时候,让线程插队执行完之后,再执行
            if (i == 100) {
                thread.join();
            }
            System.out.println("主线程"+i);
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

三、多线程对象的生命周期

1.新建状态: Thread t=new Thread(runnable) 刚new对象出来就是新建状态,此时因为没有调用start()方法,所以还没有开辟新的线程
2.就绪状态: t.start() 当Thread对象调用start()方法后,就会在内存开辟一块栈,开辟完后就进入就绪状态,此时具有抢占cpu时间片的权利
3.运行状态: 线程抢到cpu时间片后,就会执行run()方法,此时进入运行状态
4.阻塞状态: 如果在运行的过程中,碰到了sleep()或者wait()或者锁,就会中途放弃时间片,此时run()方法暂停执行,等阻塞完后,又回到就绪状态,继续抢占时间片
5.死亡状态: 当执行完run()方法了或者出异常了线程就会进入死亡状态

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/322781
推荐阅读
相关标签
  

闽ICP备14008679号