多线程---基础篇

什么是多线程

多线程指在一个程序内同时运行多个线程。
线程可以理解为轻量级的进程,它是一个基本的CPU执行单元,也是程序执行时的最小单位。一个进程内可以有多个线程同时运行。
相比进程间的切换,线程间的切换开销更小,所以多线程可以有效提升程序的执行效率。
使用多线程主要有以下优点:

  1. 提高应用程序的响应速度
    多线程可以同时执行多个任务,适当使用多线程可以使程序在等待一个线程完成某项任务时继续执行其他线程的任务,从而提高程序的响应速度。
  2. 改善资源利用率
    CPU与IO设备等资源在执行线程时如果遇到等待时间,多线程可以使其切换到另一个线程继续执行,减少资源空闲时间,提高资源利用率。
  3. 提高程序结构的模块化
    多线程程序结构更加模块化,每个线程完成一个相对独立的功能模块,这提高了程序的结构化,便于理解和修改。
  4. 并行计算
    多线程可以同时执行运算任务,充分利用多核CPU的优势,减少执行时间。
    总之,合理使用多线程可以让程序执行更高效、响应更迅速,充分利用系统资源。但也需要注意线程安全和死锁等问题。

线程、进程、多线程

任务

比如:同时吃饭、玩手机;同时开车、打电话

多线程

像下图一样:当公路上之后一个车道的时候,所有的车都必须排队进行,效率是比较慢的。那么为了解决这个问题在公路上可以开辟多条车道,这个来往的车辆就可以同步进行。在这里插入图片描述

程序、进程、线程

在操作系统中运行的程序就是进程,比如你的qq、播放器等。
在播放器程序上看视频,同时听到声音、看到图像合字幕,声音、图像、字幕都是线程
在这里插入图片描述
程序:指令和数据有序集合,其本身没有任何运行的含义,是一个静态的概念。
进程:是执行程序的一次执行过程,是一个动态概念。是系统资源分配的单位
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度的单位。

注:很多多线程是模拟出来的,真正的多线程是指多个CPU,即多核,如服务器,如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行也给代码,因为切换很多,所以就有同时执行的错觉。

线程的三种实现方式创建

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

Thread

//创建线程方式1:继承Thread类,重写run方法,调用start开启线程
public class TestTheead1 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码~"+i);
        }
    }

    //主线程
    public static void main(String[] args) {
        //创建一个线程对象
        TestTheead1 testTheead1=new TestTheead1();
        testTheead1.start();//调用start方法开启线程

        for (int i = 0; i < 2000; i++) {
            System.out.println("我在学习多线程~~~~~~"+i);
        }
    }
}

结果:
在这里插入图片描述
多线程是交替执行的,比如下图:
调用start()方法是交替执行的,也就是创建的线程和main()这个线程之间是并性的,谁先执行是CPU来控制的,线程开启不一定立即执行,由CPU调度执行
在这里插入图片描述

Runnable

//创建线程方法2,实现runnable接口,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码~"+i);
        }
    }

    //主线程
    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread3 testThread3 = new TestThread3();
        //创建线程对象,通过线程对象来开启线程
        Thread thread=new Thread(testThread3);

        thread.start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("我在学习多线程~~~~~~"+i);
        }
    }
}
Thread和Runnable总结

在这里插入图片描述

Callable接口

步骤
  1. 实现callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(3); //3个线程
  5. 提交执行:Future result1=ser.submit(t1);
  6. 获取结果:boolean r1=result1.get()
  7. 关闭服务:ser.shutdownNow()
    好处:
  8. 可以定义返回值
  9. 可以抛出异常

代码示例

public class TestCallable implements Callable<Boolean> {
    private String url;//网络图片地址
    private String name;//保存文件名称

    public TestCallable (String url,String name){
        this.url=url;
        this.name=name;
    }

    //下载图片线程的执行体
    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
        return true;
    }

    public static void main(String[] args) {
        TestCallable t1=new TestCallable("url地址","1.jpg");
        TestCallable t2=new TestCallable("url地址","1.jpg");
        TestCallable t3=new TestCallable("url地址","1.jpg");
        //创建执行服务
        ExecutorService ser=Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> r1=ser.submit(t1);
        Future<Boolean> r2=ser.submit(t1);
        Future<Boolean> r3=ser.submit(t1);

        //关闭服务
        ser.shutdownNow();
    }
}

多线程代码实例----龟兔赛跑

龟兔赛跑实例

public class Race implements Runnable{

    //胜利者
    private static String winner;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            //模拟兔子睡觉
            if(Thread.currentThread().getName().equals("兔子") && i%10==0){
                try{
                    Thread.sleep(1);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag=gameOver(i);
            //如果比赛结束了,就停止程序
            if(flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"===>跑了"+i+"步");
        }
    }
//判断是否完成比赛
    private boolean gameOver(int steps){
        //判断是否由胜利者
        if(winner!=null){
            return true;
        }{
            if(steps>=100){
                winner=Thread.currentThread().getName();
                System.out.println("winner is"+winner);
                return false;
            }
        }
        return false;
    }


    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

在这里插入图片描述

总结

  1. 线程与进程的区别
  • 进程是资源分配的最小单位,线程是CPU调度的最小单位。
  • 一个进程包含一个或多个线程,它们共享进程的资源。
  • 线程更轻量级,切换开销更小。
  1. 线程的基本使用
  • 继承Thread类或者实现Runnable接口来创建线程类。
  • 使用start()方法启动线程,而不是直接调用run()。
  • 可以给线程设置优先级、守护线程等。
  1. 线程安全与同步
  • 多线程访问共享资源需要考虑线程安全问题。
  • 使用synchronized关键字或者Lock接口来对关键代码段加锁,保证同一时刻只有一个线程执行。
  1. 线程通信
  • wait/notify/notifyAll可以实现不同线程之间的通信。
  • join方法可以等待其他线程结束。
  1. 线程池的使用
  • 线程池可以重用固定数量的线程,避免频繁创建销毁。
  • 使用Executor框架中的线程池可以更好地控制线程池的属性。
  1. 常见的多线程问题
  • 死锁、活锁、饥饿等问题及定位与解决。

以上是对多线程的一个基本认识,后续会持续更新~~
有不同见解可以指出一起学习~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

谷艳爽faye

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

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

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

打赏作者

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

抵扣说明:

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

余额充值