Java多线程基础学习笔记

本文详细介绍了Java多线程的基础知识,包括Process与Thread的区别、核心概念、三种创建线程的方式(集成Thread类、实现Runnable接口和实现Callable接口)、线程状态、线程同步与并发控制、线程池的使用等。通过实例展示了多线程操作同一资源时可能出现的数据不一致问题以及解决方案,强调了线程安全和同步的重要性。
摘要由CSDN通过智能技术生成

Process与Thread

程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念

而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位

通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。

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

核心概念

  • 线程就是独立的执行路径
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程
  • main()称之为主线程,为系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
  • 线程会带来额外的开销,如cpu调度事键,并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

三种进程创建方式

1.集成Thread类(重点)

创建线程方式一:继承Thread类,重写run方法,调用start开启线程

步骤:

  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class TestThread extends Thread {
   
    @Override
    public void run() {
   
        //run方法线程体
        for(int i = 0;i < 20;i++){
   
            System.out.println("I'm looking code " + i);
        }
    }

    public static void main(String[] args) {
   
        //main主线程
        //创建一个线程对象
        TestThread testThread = new TestThread();

        //调用start方法开启线程
        testThread.start();

        for(int i = 0;i < 20;i++){
   
            System.out.println("I'm studying thread " + i);
        }
    }
}

两条线程交替执行

运行结果:

I’m looking code 0
I’m studying thread 0
I’m looking code 1
I’m studying thread 1
I’m looking code 2
I’m looking code 3
I’m looking code 4
I’m looking code 5
I’m looking code 6
I’m looking code 7
I’m looking code 8
I’m looking code 9
I’m looking code 10
I’m looking code 11
I’m looking code 12
I’m looking code 13
I’m looking code 14
I’m looking code 15
I’m studying thread 2
I’m looking code 16
I’m looking code 17
I’m studying thread 3
I’m studying thread 4
I’m studying thread 5
I’m studying thread 6
I’m studying thread 7
I’m studying thread 8
I’m studying thread 9
I’m studying thread 10
I’m studying thread 11
I’m studying thread 12
I’m studying thread 13
I’m studying thread 14
I’m studying thread 15
I’m studying thread 16
I’m studying thread 17
I’m studying thread 18
I’m studying thread 19
I’m looking code 18
I’m looking code 19

注意:线程开启不一定立即执行,由CPU安排调度

thread练习:实现多线程同步下载

需要引入commons-io依赖:

<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.6</version>
</dependency>

编写线程类:

public class TestThread2 extends Thread {
   
    private String url;     //url地址
    private String name;    //文件名

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

    @Override
    public void run() {
   
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("download " + name);
    }

    public static void main(String[] args) {
   
        TestThread2 t1 = new TestThread2("https://img-home.csdnimg.cn/images/20211210050448.jpg","1.jpg");
        TestThread2 t2 = new TestThread2("https://img-bss.csdn.net/1635839557569.jpg","2.jpg");
        TestThread2 t3 = new TestThread2("https://img-bss.csdn.net/1639039638150.png","3.png");

        t1.start();
        t2.start();
        t3.start();
    }


}

class WebDownloader{
   
    public void downloader(String url, String name){
   
        try {
   
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
   
            e.printStackTrace();
            System.out.println("IO Exception,downloader error");
        }
    }
}

我们预测的执行顺序,可能是先t1,再t2,最后t3。其实是这三个进程同时执行,没有先后顺序,运行结果也不固定。

运行结果(多种情况):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xSJiC9Ya-1639450526884)(C:\Users\17863\AppData\Roaming\Typora\typora-user-images\image-20211211191650324.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wxtG7qL0-1639450526886)(C:\Users\17863\AppData\Roaming\Typora\typora-user-images\image-20211211191708075.png)]

2.实现Runnable接口(重点)

步骤:

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class TestThread3 implements Runnable {
   
    @Override
    public void run() {
   
        for(int i = 0;i < 20;i++){
   
            System.out.println("I'm looking code " + i);
        }
    }

    public static void main(String[] args) {
   
        //创建runnable接口的实现类对象
        TestThread3 testThread3 = new TestThread3();
        new Thread(testThread3).start();

        for(int i = 0;i < 20;i++){
   
            System.out.println("I'm studying thread " + i);
        }
    }
}

运行结果:

I’m studying thread 0
I’m looking code 0
I’m studying thread 1
I’m looking code 1
I’m looking code 2
I’m studying thread 2
I’m looking code 3
I’m studying thread 3
I’m looking code 4
I’m studying thread 4
I’m looking code 5
I’m studying thread 5
I’m looking code 6
I’m studying thread 6
I’m looking code 7
I’m studying thread 7
I’m looking code 8
I’m studying thread 8
I’m looking code 9
I’m studying thread 9
I’m studying thread 10
I’m studying thread 11
I’m studying thread 12
I’m studying thread 13
I’m studying thread 14
I’m looking code 10
I’m looking code 11
I’m looking code 12
I’m looking code 13
I’m looking code 14
I’m looking code 15
I’m looking code 16
I’m looking code 17
I’m studying thread 15
I’m looking code 18
I’m studying thread 16
I’m studying thread 17
I’m looking code 19
I’m studying thread 18
I’m studying thread 19

仍是同时交替执行

thread练习:实现多线程同步下载

引入commons-io:

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>

编写线程类:

public class TestThread2 implements Runnable {
   
    private String url;     //url地址
    private String name;    //文件名

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

    @Override
    public void run() {
   
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("download " + name);
    }

    public static void main(String[] args) {
   
        TestThread2 t1 = new TestThread2("https://img-home.csdnimg.cn/images/20211210050448.jpg","1.jpg");
        TestThread2 t2 = new TestThread2("https://img-bss.csdn.net/1635839557569.jpg","2.jpg");
        TestThread2 t3 = new TestThread2("https://img-bss.csdn.net/1639039638150.png","3.png");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }


}

class WebDownloader{
   
    public void downloader(String url, String name){
   
        try {
   
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
   
            e.printStackTrace();
            System.out.println("IO Exception,downloader error");
        }
    }
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APVXuapx-1639450526886)(C:\Users\17863\AppData\Roaming\Typora\typora-user-images\image-20211211192925142.png)]

对比继承Thread类和实现Runnable接口两种方法

继承Thread类:

  • 子类继承Thread类具备多线程能力
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性

实现Runnable接口:

  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

初识并发问题

举一个多线程操作同一对象的例子

模拟买火车票的场景

public class TestThread4 implements Runnable {
   
    //票数
    private int ticketNums = 10;

    @Override
    public void run() {
   
        while(true){
   
            if(ticketNums <= 0){
   
                break;
            }
            System.out.println(Thread.currentThread().getName() + "拿到了第 " + ticketNums-- + " 张票");
        }
    }

    public static void main(String[] args) {
   
        TestThread4 ticket = new TestThread4();
        new Thread(ticket,"user1").start();
        new Thread(ticket,"student").start();
        new Thread(ticket,"teacher").start();
    }
}

运行结果:

user1拿到了第 9 张票
teacher拿到了第 9 张票
student拿到了第 10 张票
teacher拿到了第 7 张票
user1拿到了第 8 张票
teacher拿到了第 5 张票
student拿到了第 6 张票
teacher拿到了第 3 张票
teacher拿到了第 1 张票
user1拿到了第 4 张票
student拿到了第 2 张票

可以发现一个问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱

龟兔赛跑

  1. 首先来个赛道距离,然后要离终点越来越近
  2. 判断比赛是否结束
  3. 打印出胜利者
  4. 龟兔赛跑开始
  5. 故事中是乌龟赢的,兔子需要睡觉,所以我们来模拟兔子睡觉
  6. 终于,乌龟赢得比赛
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 % 50 == 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;
        } else {
   
            if(steps >= 100){
   
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
   
        Race race = new Race();

        new Thread(race,"兔子").start();
        new Thread(race,"乌龟"
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

九天漩女

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

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

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

打赏作者

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

抵扣说明:

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

余额充值