Java 多线程学习(一)

简介: Java 多线程学习

1、线程简介

1.1 多任务理解

我们举例来理解下多任务;

开车 + 打电话

吃饭 + 玩手机

这些动作都可以抽象为任务,虽然看起来一心二用,但人只有一个大脑,在一个时间片刻只能处理一个任务。

CPU 也是一样,面对多个任务,只能在一个时间片刻处理一个任务。

1.2 多线程理解

我们举例来理解下多线程;

多条线路同时跑起来;有多个分支共同去执行;

主线程调用 run 方法和调用 start 方法开启子线程的区别如下图所示。

1.3 线程与进程

执行程序的过程,叫进程;一个进程中有多个线程;

核心概念;

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

2、线程实现

线程的三种实现方式:(线程实现在实际工作中优先使用Runnable接口和Callable接口方式

2.1 第一种:继承 Thread 类,重写 run 方法

继承 Thread 类,重写 run 方法。创建这个类的对象,再调用 start() 即可

package com.example.democrud.democurd.test01;
/**
 * @author 闫文超
 */
//多线程调用 先继承Thred 方法 重写run方法 最后再用start 开启线程
//总结:线程的开启不一定立即开始执行,由CPU调度执行
public class testThread01 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程---" + i);
        }
    }
    public static void main(String[] args) {
        //创建一个线程对象
        testThread01 thread01 = new testThread01();
        //先执行run的方法在执行下面的内容
        // thread01.run();
        //start的方法和main同步执行;顺序同步
        // 调用start 开启线程
        thread01.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习thread--" + i);
        }
    }
}

下载文件需要在 pom.xml 中 commons io 包。

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
package com.example.democrud.democurd.test01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class testThred02 extends Thread {
    public String url;//图片路径
    public String name;//图片的名字
    public testThred02(String url, String name) {
        this.name = name;
        this.url = url;
    }
    @Override //代表重写
    public void run() {
        downloadpng down = new downloadpng();
        down.download(url, name);
        System.out.println("下载的文件名为" + name);
    }
    //主线程 main方法
    public static void main(String[] args) {
        testThred02 thred01 = new testThred02("https://img1.baidu.com/it/u=413643897,2296924942&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1680541200&t=26be472b80013591100109eb63c7c5ec", "test01.jpg");
        testThred02 thred02 = new testThred02("https://img1.baidu.com/it/u=307074048,654359288&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1680541200&t=554384a7bbd003adba3de4aaa73365d4", "test02.jpg");
        testThred02 thred03 = new testThred02("https://img1.baidu.com/it/u=307074048,654359288&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1680541200&t=554384a7bbd003adba3de4aaa73365d4", "test03.jpg");
        //开启线程
        thred01.start();
        thred02.start();
        thred03.start();
    }
    //先写一个下载器
    class downloadpng {
        //引用地址和名字进行下载
        public void download(String url, String name) {
            try {
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO异常,downloader 方法出现问题");
            }
        }
    }
}

使用该方法下载网络图片。

2.2 第二种:

继承 Runnable 接口,创建 Tread 对象
或者使用Runnable接口通过线程池
下面演示的是继承 Runnable 接口,创建 Tread 对象:
继承 Runnable 接口,创建 Tread 对象,传入实现类,开启 start 方法

package com.example.democrud.democurd.test01;
//1.实现Runnable接口 重写run方法 执行线程需要丢人Runnable接口实线路调用start方法启动线程
public class testThread03 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接口的实现类对象
        testThread03 thread03 = new testThread03();
        //创建线程对象,通过线程对象来开启我们的线程;代理
        Thread thread = new Thread(thread03);
        thread.start();
        // 或者
       // new Thread(thread03).start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习当当中" + i);
        }
    }
}

以上两种方式的比较:

继承 Thread 类

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

实现 Runnable 接口

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

火车抢票实例:

Runnable 实现多线程,创造一个实列 ticketRunnable ,可共享给多个线程。

package com.example.democrud.democurd.test01;
//实现Runnnable的方式调用多线程
//多个线程调用一个对象
//    买火车票的例子
//    发现问题:多个线程操作同一个资源,线程不安全,数据紊乱!
public class testThread04 implements Runnable{
    public int ticketNums=30;
    @Override
    public void run() {
        while (true){
            if (ticketNums<=10){
                break;
            }
            try {
                //线程模拟休眠2s
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        }
    }
    public static void main(String[] args) {
        testThread04 thread04 = new testThread04();
        new Thread(thread04,"小王").start();
        new Thread(thread04,"小龙").start();
        new Thread(thread04,"小李").start();
        new Thread(thread04,"黄牛").start();
    }
}

龟兔赛跑案例:

package com.example.democrud.democurd.test01;
//实现runnable的接口
public class testThred05 implements Runnable {
    //胜利者
    public static String winner;
    @Override
    public void run() {
        for (int i = 0; i < 1001; i++) {
            if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
                //每跑10步 兔子休眠0.5毫秒
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Boolean gameover = gameover(i);
            if (gameover)
                break;
            System.out.println(Thread.currentThread().getName() + "-跑了" + i);
        }
    }
    public Boolean gameover(int steps) {
        if (winner != null) {
            return true;
        } else {
            if (steps >= 1000) {
                winner = Thread.currentThread().getName();
                System.out.println("winner is" + winner);
                return true;
            }
        }
        return false;
    }
    public static void main(String[] args) {
        testThred05 thred05 = new testThred05();
        new Thread(thred05, "兔子").start();
        new Thread(thred05, "乌龟").start();
    }
}


相关文章
|
5天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
4天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
4天前
|
Java 开发者
Java多线程编程的艺术与实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的技术文档,本文以实战为导向,通过生动的实例和详尽的代码解析,引领读者领略多线程编程的魅力,掌握其在提升应用性能、优化资源利用方面的关键作用。无论你是Java初学者还是有一定经验的开发者,本文都将为你打开多线程编程的新视角。 ####
|
3天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
9天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
32 9
|
6天前
|
安全 Java 开发者
Java多线程编程中的常见问题与解决方案
本文深入探讨了Java多线程编程中常见的问题,包括线程安全问题、死锁、竞态条件等,并提供了相应的解决策略。文章首先介绍了多线程的基础知识,随后详细分析了每个问题的产生原因和典型场景,最后提出了实用的解决方案,旨在帮助开发者提高多线程程序的稳定性和性能。
|
12天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
9天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
12天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
26 3
|
11天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
下一篇
无影云桌面