Java 多线程介绍及线程创建

导读:本篇文章讲解 Java 多线程介绍及线程创建,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

⭐写在前面⭐

🧭Java 多线程
📢今天我们进行 Java 多线程介绍及线程创建 的学习,感谢你的阅读,内容若有不当之处,希望大家多多指正,一起进步!!!
♨️如果觉得博主文章还不错,可以👍三连支持⭐一下哦😀
请添加图片描述

Java 多线程介绍及线程创建

线程相关概念

程序

程序是为完成特定任务、用某种语言编写的一组指令的集合。简单的来说,就是我们写的代码

在这里插入图片描述

进程

进程是程序的一次执行过程,或是正在运行的一个程序,是一个 动态 的过程。比如我们启动QQ,就启动了一个进程,操作系统就会为该进程分配内存空间系统资源,再次启动谷歌浏览器,就又启动一个进程,操作系统也将会为其分配内存空间系统资源。我们可以通过任务管理器来查看和管理进程。

在这里插入图片描述
在这里插入图片描述

线程

线程是由进程创建的,是进程的一个实体,可以理解为线程是进程的一条执行路径,一个进程可以拥有多个线程。比如我们使用QQ,可以向好友发消息,同时也可以接收消息,再同时还可以视频语音聊天,这都是线程帮我们完成的。

在这里插入图片描述

并发

并发同一时刻,多个任务交替执行,造成一种“貌似同时执行”的错觉,在早期的计算机系统中,CPU是单核的,在同一时刻只能执行一个进程,但是我们也可以一边聊天🐧一边🌨️网易云🎶,这就是CPU轮流为进程服务的结果,只不过CPU切换进程速度特别快,我们察觉不到而已!!

在这里插入图片描述

并行

并行:同一个时刻,多个任务同时执行,多核CPU可以实现同时执行多个任务。
在这里插入图片描述

获取当前电脑CPU核数

调用Runtime类下的availableProcessors方法可以获取当前电脑CPU核数

public class CpuNumber {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        int i = runtime.availableProcessors();
        System.out.println("当前电脑CPU核数为:" + i);
    }
}

运行结果:
在这里插入图片描述

创建线程

在Java中创建线程有三种方式:
● 继承Thread类
● 实现Runnable接口
● 实现Callable接口

继承Thread类

Thread类

class Thread implements Runnable

Thread类是Runnable接口的一个实现类,它重写了Runnable接口的Run方法,一个类可以继承Thread类重写其 Run 方法来创建一个线程。

案例: 编写代码,用继承Thread类的方式创建一个线程,每隔1s打印一个”a”及其线程名,共打印5次,同时显示第几次打印。在主线程中每隔1.5s打印一个”主线程”及其线程名,共打印3次,同时显示第几次打印。

public class TestThread01 {
    public static void main(String[] args) {
        A a = new A();
        a.start();//开启线程

        //打印主线程
        int count = 0;
        while (true) {
            System.out.println("主线程" + Thread.currentThread().getName() + "第" + ++count + "次");
            
            if (count == 3) {
                break;
            }

            try {
                Thread.sleep(1500); //休眠1.5秒钟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

class A extends Thread{
    @Override
    public void run() {
        int count = 0;
        while (true) {
            System.out.println("a " + Thread.currentThread().getName() + "第" + ++count + "次");
            
            if (count == 5) {//打印5次退出
                break;
            }

            try {
                Thread.sleep(1000);//休眠1秒钟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


运行结果:
在这里插入图片描述

主线程和Thread-0线程交替执行。在主方法中,并没有因为Thread-0线程的执行而阻塞,此时两个线程在并发执行。
在这里插入图片描述

start方法

在上述示例中,我们并没有在主方法中调用A类中的Run方法,那么是如何启动一个线程的呢?
事实上,我们如果直接调用Run方法的话,并不会启动一个线程,只是调用了一个方法而已,当然也不会出现交替执行的现象,程序会阻塞那里,此时只有一个主线程,程序会顺序向下执行,图示如下:
在这里插入图片描述
那么问题来了,我们如何是通过start方法来启动线程的呢?
查看源码:

public synchronized void start() {
  
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }

    private native void start0();

通过源码发现,我们发现在start方法中会调用一个start0方法,而这个start0方法是一个native修饰的本地方法,其底层是用C++实现的,所以run方法是底层帮我们调用的,归根其底在Java中创建一个线程还是通过底层实现的
在这里插入图片描述

实现Runnable接口

Runnable接口

public interface Runnable {
    public abstract void run();
}

Runnable接口的一个实现类重写其Run方法可以创建一个线程。

案例: 编写代码,用实现Runnable接口的的方式创建一个线程,每隔1s打印一个”b”及其线程名,共打印5次,同时显示第几次打印。在主线程中每隔1.5s打印一个”主线程”及其线程名,共打印3次,同时显示第几次打印。

public class TestThread02 {
    public static void main(String[] args) {
        B b = new B();
        Thread thread = new Thread(b);
        thread.start();//开启一个线程

        //主线程打印
        int count = 0;
        while (true) {
            System.out.println("主线程" + Thread.currentThread().getName() + "第" + ++count + "次");

            if (count == 3) {//打印3次退出
                break;
            }

            try {
                Thread.sleep(1500); //休眠1.5秒钟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class B implements Runnable {

    @Override
    public void run() {
        int count = 0;
        while (true) {
            System.out.println("b " + Thread.currentThread().getName() + "第" + ++count + "次");

            if (count == 5) {//打印5次退出
                break;
            }

            try {
                Thread.sleep(1000);//休眠1秒中
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

运行结果:
在这里插入图片描述
🚀解读
在主方法中,我们是通过把B类的一个实例交给了Thread的一个实例,然后调用start方法来启动一个线程的。那么为什么要交给Thread类的实例呢?其实在这里Thread类充当了一个代理类的身份。

查看源码

Thread类

public class Thread implements Runnable {
	private Runnable target;
	
	@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

通过源码我们发现,Thread类中的Run方法中有一个target对象,而这个target对象其实就是我们在创建Thread实例时传入的对象。当Thread调用start方法后,会在start方法中调用本地方法start0,然后会在底层调用其run方法,最后在run方法中会通过动态绑定调用run方法执行业务逻辑。
在这里插入图片描述

实现Callable接口

Callable接口

Callable接口中提供了一个call方法,该方法会抛出一个异常,该接口通过泛型来定义。

public interface Callable<V> {
    V call() throws Exception;
}

该接口的实现类是无法直接使用,需要借助于FutureTask类,该类能接收Callable接口的实现类。
在这里插入图片描述

public class FutureTask<V> implements RunnableFuture<V> {
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
}

案例: 编写代码,用实现Callable接口的的方式创建一个线程,每隔1s打印一个”c”及其线程名,共打印5次,同时显示第几次打印。在主线程中每隔1.5s打印一个”主线程”及其线程名,共打印3次,同时显示第几次打印。

import java.io.Flushable;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class TestThread03 {
    public static void main(String[] args) {
        C c = new C();
        FutureTask<String> futureTask = new FutureTask<String>(c);
        Thread thread = new Thread(futureTask);
        thread.start();//开启线程


        //打印主线程
        int count = 0;
        while (true) {
            System.out.println("主线程" + Thread.currentThread().getName() + "第" + ++count + "次");

            if (count == 3) {
                break;
            }

            try {
                Thread.sleep(1500); //休眠1.5秒钟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

class C implements Callable {

    @Override
    public Object call() throws Exception {
        int count = 0;
        while (true) {
            System.out.println("c " + Thread.currentThread().getName() + "第" + ++count + "次");

            if (count == 5) {//打印5次退出
                break;
            }

            try {
                Thread.sleep(1000);//休眠1秒中
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

运行结果:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/95502.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!