栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java 多线程介绍及线程创建

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java 多线程介绍及线程创建

⭐写在前面⭐

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

文章目录

Java 多线程介绍及线程创建

线程相关概念

程序进程线程并发并行获取当前电脑CPU核数 创建线程

继承Thread类

Thread类start方法 实现Runnable接口

Runnable接口 实现Callable接口

Callable接口

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 call() throws Exception;
}

该接口的实现类是无法直接使用,需要借助于FutureTask类,该类能接收Callable接口的实现类。

public class FutureTask implements RunnableFuture {
    public FutureTask(Callable 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 futureTask = new FutureTask(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;
    }
}

运行结果:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/780699.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号