多线程——初步认识线程

线程是什么?

在我一开始学习多线程的时候,一直很疑惑的是,为什么要创造多线程,线程又是什么?其实很多时候问题就藏在我们的生活当中,我们也可以把编程的思想与现实生活联系起来就可以理解为什么Java之父要去专门设计线程。

想象一下在你的日常生活中,一定离不开做家务,我们都会有一套自己的流程,先扫地再拖地,但你自己没办法同时做这两件事情,只能做一件,这其实跟线程的定义很类似,也就是执行一件事情,在计算机的世界里,线程就和我们一样,负责自己要执行的代码,也就是说一个线程就是一个“执行流”

经常干活的友友们应该都知道,要想提高效率,那必须得合作,正所谓男女搭配干活不累,虽然很土但很有效,只要在我们扫地后立马有一个人能在我们屁股后面卖命拖地,那效率就是杠杠滴。但我们两个是独立的个体,做的工作也不一样,我扫地他拖地,也就是同时进行但互不干扰。在我们计算机的世界也同样如此,多线程的本质就是多个“执行流”并发进行

在我们日常生活中,不免要去银行办理业务,所有的银行都会设置服务窗口,其本质也是多线程,在办理业务的时候,如果只有一个服务人员的话,效率是明显很低的,如果办理的人员增多,那业务量就会被积压,不能按时有效的完成任务,如果多设立几个柜台,就可以大大减少时间开销,提高我们办理业务的效率,但本质还是帮助银行办理业务,所以多线程的意义就在于将大的任务拆分成若干个小的任务,将这些小的任务交给各自的线程,也就是“执行流”去完成,其中分配任务的线程又被称为主线程

线程与进程的区别

现在我相信友友们应该对多线程有一个基本的了解,但还有一个关键的要素,就是进程,进程也是一个很重要的知识内容,它涉及到关于计算机硬件的发展。

经常打游戏的友友们应该都知道,当自己的游戏卡死的时候,最好的方式是直接打开任务管理器,手动进行结束程序,其实这个过程就是销毁一个进程,由此我们可以知道,一个进程就是一个程序的运行,它是由操作系统分配给每个运行程序的使用资源的权限,当我们停止进程,就意味着操作系统不再给该程序提供可分配的使用资源,也就是关闭可使用的内存空间。有关进程的讲解还有很多详细的地方,比如寄存器和堆栈等,以及更深入的学习,就等到之后再去进行详细的说明。

在我们CPU诞生到发展的几十年以来,CPU的制作工艺也越来越精湛,从之前的单核CPU到现在多核CPU,我们计算机的性能也是在逐步的提升,那为什么我们会感觉到在使用高性能计算机的时候的体验会大大增加,很大一部分的原因就是CPU的性能提升。这其中有很多方面,其中最主要的方面就是CPU调用进程的效率,在古老时期,我们的操作系统还没有那么完善,一个操作系统只能运行一个进程,等到这个进程结束才能运行下一个进程,而CPU就是来选择调用的进程来运行。

现在主流的操作系统都支持多个进程同时运行,互不干扰,每个程序都可以在自己的虚拟内存中执行,而CPU的进化更是大大提高处理任务的效率,核心数越多就意味着单独执行指令的BOOS就越多,那分配给操作系统的任务的速度就越快。

那说了这么多,大家也看累了,其实我们也就需要知道进程是一个很重要的计算机术语,它是系统分配资源的最小单位就可以了。

那有聪明的小伙伴就发现了,那线程和进程的区别就在于使用的范围上,还是拿上面的例子接着说,一个地方有三家银行,都可以办理业务,当人多的时候,友友们肯定想快点,但你绝对不会傻到去其他两家银行办理取钱业务,因为你只能在这家银行使用他们的银行卡,也就是说虽然都是银行,但任务资源管理是不同的,他们的数据并不相同,但你可以在这家银行的不同柜台办理同一种业务,不会受到干扰,因为他们都是在调用并执行这家银行的资源,本质来说就是同时为这一家银行来服务。因此我们就可以理解进程是系统分配资源的最小单位,线程是系统调度的最小单位。

创建线程

理论结束,到了最喜欢的实战部分,要想真正弄明白多线程在Java中的运用,那必须得恨恨地练习代码,正所谓好记性不如烂笔头,只有多敲代码才能更好的去进行实际的开发。

我们创建线程有很多种方法,在大家初步学习的阶段,就先给友友们介绍几个简单的创建线程的方法。

方法一:继承Thread类

咱们可以继承Thread来创建一个线程类

// 创建一个自己的Thread类继承Thread
class myThread extends Thread {
    // run()方法是执行线程逻辑
    @Override
    public void run() {

        while (true) {
            System.out.println("我是Thread线程");
            // 父类的run方法没有throws,子类的run方法就也没有throws
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}
public class demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new myThread();
        
        // 启动Thread类线程
        t.start();
        while (true) {
            System.out.println("我是main线程");
            Thread.sleep(1000);
        }
    }
}
 

可以发现我们的程序每隔一秒就会弹出文字信息,但是你会惊奇的发现线程是随机执行的,这跟操作系统的调度算法有关,因为多线程的执行是由操作系统里的调度器决定的,main线程和Thread线程他们都是并发执行,都想第一个执行,这样的行为就会产生竞争,这跟线程的优先级也有一定的关系,但优先级高的线程不一定每次都会比低优先级的线程先执行,跟CPU的处理能力还有寄存器有关,这些就等到之后再进行详细的解答。

我们还可以使用匿名内部类的方法创建多线程

public class demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("我是Thread线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
               
            }
        };

        t.start();
        while(true) {
            System.out.println("我是main线程");
            Thread.sleep(1000);
        }
    }
}

我们还可以使用lambda表达式的方法创建多线程

public class demo3{
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(true) {
                System.out.println("我是Thread线程");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
        while(true) {
            System.out.println("我是main线程");
            Thread.sleep(1000);
        }
    }
}

方法二:实现Runnable接口

和上面的大差不差,先给你们看看整体的代码

    //  实现Runnable接口
class MyRunnable implements Runnable {

    //重写run方法
    @Override
    public void run() {
        while (true) {
            System.out.println("我是Thread线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class demo4 {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个runnable对象
        MyRunnable myRunnable = new MyRunnable();
        // 将重写的run方法放进Thread类里
        Thread t = new Thread(myRunnable);
        // 开启线程
        t.start();
        while(true) {
            System.out.println("我是main线程");
            Thread.sleep(1000);
        }
    }
}
public class demo5 {
    public static void main(String[] args) throws InterruptedException {
        Runnable run = new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("我是Thread线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        Thread t = new Thread(run);
        t.start();
        while(true) {
            System.out.println("我是main线程");
            Thread.sleep(1000);
        }
    }
}
public class demo6 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
                while(true) {
                    System.out.println("我是Thread线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

        t.start();
        while(true) {
            System.out.println("我是main线程");
            Thread.sleep(1000);
        }
    }
}

Threadl类和Runnable接口的区别

这是我们目前介绍的两种简单的创建多线程的方法,他们也有不一样的地方,细心的友友们或许已经发现了些许端倪。

Thread类简单直观,可以用于简单的线程任务,但缺点一样很明显,由于Java的特性导致了不能像C++一样有多继承,所以继承了Thread类的子类就不能够再去继承其他类了。

Runnable接口则可以为我们提供更多的选择,避免了单继承的限制,可以实现多个线程共享相同的任务代码。

public class SharedResource implements Runnable {
    private int count = 0;

    public void run() {
        for (int i = 0; i < 5; i++) {
            incrementCount();
            System.out.println(Thread.currentThread().getName() + " - Count: " + count);
            try {
                Thread.sleep(1000); // 模拟一些耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void incrementCount() {
        count++;
    }

    public static void main(String[] args) {
        SharedResource sharedResource = new SharedResource();

        // 创建两个线程并共享同一个Runnable实例
        Thread thread1 = new Thread(sharedResource, "Thread 1");
        Thread thread2 = new Thread(sharedResource, "Thread 2");

        thread1.start();
        thread2.start();
    }
}

在这个示例中,ShareResource类实现了 Runnable 接口,其中包含一个共享的计数器count。在 main方法中,我们创建了两个线程thread1thread2,它们共享了同一个ShareResource实例 ShareResource。每个线程在执行时,都会调用 incrementCount方法来增加 count的值,并输出当前的计数器值。

由于incrementCount方法被声明为 synchronized,它确保了在任何时刻只有一个线程可以执行该方法,避免了多线程同时修改 count变量可能引发的并发问题。

通过运行这个示例代码,你可以看到两个线程共享了相同的 Runnable 实例,并且可以同时访问和修改共享的数据。这展示了 Runnable 接口灵活性的优势,使多个线程可以更有效地共享资源和执行任务。

关于synchronized我们在后面的内容会详细介绍,这一块仅做一个了解。

Java线程和操作系统线程的关系

其实还有一点应该和大家强调,也是为了后面友友们的学习,就是还得区分一下Java线程和操作系统线程的不同。

我们都知道Java程序的运行需要在JVM上,也就是Java虚拟机上去执行,这是整个Java语言体系的重中之重,而JVM就是负责管理Java程序中的线程。在Java中创建的每一个线程都会映射到操作系统的原生线程上,操作系统会负责调度这些原生线程的执行。听起来有点像JVM有点像中介公司,用来处理线程和操作系统之间的关系,事实确实是如此,JVM还有很多强大的功能,这个就等之后再慢慢细说。

那JVM是怎么做到在不同的操作系统使用同一种语言进行创建和销毁线程的呢?原因也很简单,可以联想一下秦始皇统一六国后,做的几件事,车同轨书同文,打败语言不通地方不通的最好方式就是统一进行管理,放在我们的Java语言里也是如此,JVM内部提供了一套API,可以将开发人员的代码有效地给不同的操作系统去执行,但其中的原理很复杂,我觉得友友们也不会想去看源码,博主我也是看得云里雾里,但我们可以进行了解,不要把Java线程和操作系统的线程混为一谈了。

小结

希望友友们看了博主的文章能够初步理解和运用多线程,之后还会继续产出多线程有关的文章供大家系统性的学习。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

comerun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值