Java多线程的用法详解

Java多线程的用法详解

Java多线程是Java编程中非常重要的一个部分,在Java中通过多线程实现并发编程,提高程序的执行效率与响应能力,本文将详细讲解Java多线程的用法。

为什么需要多线程?

在介绍Java多线程之前,我们需要了解为什么需要多线程。首先,操作系统分给每个进程固定的资源是有限的,而非多线程的单进程只能利用其中一个CPU并执行一个任务。而多线程的程序可以分裂成许多子任务且各自独立执行,可充分利用CPU时间片,从而提高CPU的利用率,加速程序的执行。

另外,一些需要处理的操作是耗时且独立的,例如:读写文件或接收网络请求等,这些操作使用单线程,将导致程序在等待这些操作完成时阻塞,从而浪费CPU资源。而使用多线程,可以充分利用CPU的其他资源,提高程序的效率。

Java多线程的用法

Java通过封装实现了多线程,并提供了许多API来支持多线程编程。

创建线程

Java中线程的创建方式有两种:

  1. 继承Thread类
  2. 实现Runnable接口

继承Thread类

class MyThread extends Thread {
    public void run() {
        System.out.println("这是一个新线程!");
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();  // 创建一个新线程
        myThread.start();  // 启动线程
        System.out.println(Thread.currentThread().getName() + "线程已结束。");
    }
}

输出:

这是一个新线程!
main线程已结束。

实现Runnable接口

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("这是一个新线程!");
    }
}

public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();  // 创建一个Runnable对象
        Thread thread = new Thread(myRunnable);  // 创建一个新线程
        thread.start();  // 启动线程
        System.out.println(Thread.currentThread().getName() + "线程已结束。");
    }
}

输出:

这是一个新线程!
main线程已结束。

线程的生命周期

Java中的线程生命周期可分为以下五个状态:

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程创建后,其他线程调用了该线程的 start() 方法,该线程进入就绪状态。
  3. 运行状态(Running):就绪状态的线程获得 CPU 时间,执行 run() 方法。
  4. 阻塞状态(Blocked):阻塞状态指线程因为某种原因放弃了 CPU 的使用权,暂时停止运行,直到线程进入就绪或结束状态。例如:等待IO操作、调用 sleep() 方法等。
  5. 结束状态(Dead):线程执行完了 run() 方法,或因异常退出了 run() 方法,该线程结束生命周期。

线程调度

Java提供了一些 API 来实现线程的调度。

sleep()

sleep() 方法可以让一个线程暂停执行,进入阻塞状态。它有两个重载方法,分别是:

public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException

第一个方法指挂起线程 ms 时间,第二个方法指挂起线程 ms 毫秒 + ns 纳秒时间。需要注意的是这两个方法会抛出的是 InterruptedException 异常,需要通过 try-catch 语句捕获。

public class SleepDemo {
    public static void main(String[] args) {
        try {
            System.out.println("开始执行");
            Thread.sleep(3000);  // 线程休眠3秒
            System.out.println("结束执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出:

开始执行
// 等待3秒时间
结束执行

yield()

yield() 方法可以让当前线程让出 CPU 时间片,进行调度。调度器可能会决定将 CPU 时间片分配给这个线程下一次调用。

public class YieldDemo {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程1执行:" + i);
                Thread.yield(); // 让出 CPU 调度
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程2执行:" + i);
            }
        });
        thread1.start();
        thread2.start();
    }
}

输出:

线程1执行:0
线程2执行:0
线程1执行:1
线程2执行:1
线程1执行:2
线程2执行:2
线程1执行:3
线程2执行:3
线程1执行:4
线程2执行:4

join()

join() 方法可以让一个线程等待另一个线程执行结束。假设当前线程 A 执行了 threadB.join(),则当前线程 A 将进入阻塞状态,直到 threadB 线程执行结束。

public class JoinDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("线程1开始执行:");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1结束执行:");
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("线程2开始执行:");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2结束执行:");
        });
        thread1.start(); // 启动线程1
        thread2.start(); // 启动线程2
        thread1.join();  // 等待线程1结束
        thread2.join();  // 等待线程2结束
        System.out.println("所有线程执行结束。");
    }
}

输出:

线程1开始执行:
线程2开始执行:
线程2结束执行:
线程1结束执行:
所有线程执行结束。

线程同步

多线程在同时访问共享资源时可能会出现数据冲突的情况,这时就需要使用线程同步来保证线程安全。

synchronized关键字

synchronized 关键字可用于修饰一个方法或代码块,当其它线程想要访问该代码块时,必须先获得相应的锁。如果该锁被其它线程占用,则需要等待直到该线程释放锁。

public class SynchronizedDemo {
    private volatile static int COUNT = 0;
    private static Object lock = new Object();
    private static void increment() {
        synchronized (lock) {
            COUNT++;
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                increment();
            }).start();
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("COUNT = " + COUNT);
    }
}

输出:

COUNT = 100

Lock接口

Java提供了Lock接口实现线程同步,它比synchronized更加灵活,可适用于复杂场景。

public class LockDemo {
    private volatile static int COUNT = 0;
    private static Lock lock = new ReentrantLock();
    private static void increment() {
        lock.lock();
        try {
            COUNT++;
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                increment();
            }).start();
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("COUNT = " + COUNT);
    }
}

输出:

COUNT = 100

总结

本文详细介绍了Java多线程的用法,包括线程创建、线程的生命周期、线程调度和线程同步。希望通过本文的学习,能够有助于对Java多线程的理解和运用。

示例

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程的用法详解 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Spring Boot超详细分析启动流程

    以下是“Spring Boot超详细分析启动流程”的完整攻略: 目录 准备工作 Spring Boot 启动流程分析 自定义启动流程 示例1:加载自定义配置文件 示例2:自定义Banner 准备工作 在分析 Spring Boot 启动流程之前,我们需要先了解几个基本概念: SpringApplicationBuilder:Spring Boot 启动入口,…

    Java 2023年5月15日
    00
  • Mybatis之动态sql标签的使用

    那么首先我们先讲一下什么是Mybatis的动态sql标签。动态sql标签可以根据传递的参数生成不同的SQL查询语句,提供更加灵活的查询方式。相对于其他ORM框架,Mybatis的动态sql标签有独特的实现方式。那么接下来我们来看看如何使用Mybatis的动态sql标签。 判断语句标签<if> 我们可以使用<if>标签来进行条件判断。例…

    Java 2023年5月20日
    00
  • Java实现字符串匹配的示例代码

    下面是Java实现字符串匹配的示例代码的完整攻略: 1. 什么是字符串匹配 字符串匹配指在一个字符串中查找另一个字符串的过程。在计算机科学中,字符串匹配是十分常见的问题,例如用来搜索文本文件中的单词、在数据库中查询某些记录等等。这里我们介绍一种常见的字符串匹配算法——KMP算法。 2. KMP算法介绍 KMP算法全称是Knuth-Morris-Pratt算法…

    Java 2023年5月26日
    00
  • JavaWeb文件上传与下载功能解析

    JavaWeb文件上传与下载功能解析 文件上传功能 在JavaWeb中,文件上传主要包括三个部分:前端页面、后端处理、文件保存。 前端页面 文件上传的前端页面需要使用form表单,同时表单属性需要设置为enctype=”multipart/form-data”,以支持文件上传。例如: <form action="upload" me…

    Java 2023年5月19日
    00
  • Extjs中通过Tree加载右侧TabPanel具体实现

    实现“Extjs中通过Tree加载右侧TabPanel”需要以下步骤: 创建一个Ext.tree.Panel,用于显示树形结构,其中需要配置store,root等属性。 示例代码: Ext.create(‘Ext.tree.Panel’, { store: yourTreeStore, root: { text: ‘Root’, expanded: true…

    Java 2023年6月15日
    00
  • JavaWeb实现图形报表折线图的方法

    下面就是JavaWeb实现图形报表折线图的方法的完整攻略: 1. 准备工作 在实现JavaWeb图形报表折线图前,我们需要先准备好以下资源: 前端使用的图表库,例如ECharts、Highcharts等; 后端使用的JavaWeb框架,例如Spring、Struts2等; 数据库,用于存储数据; 数据库连接池,用于连接数据库。 2. 使用ECharts绘制折…

    Java 2023年6月15日
    00
  • 使用IntelliJ IDEA2020.2.2 x64 新建java项目并且输出Hello World

    下面我会详细讲解使用IntelliJ IDEA 2020.2.2 x64新建Java项目并输出”Hello World”的完整攻略。 步骤1:下载和安装IntelliJ IDEA 首先你需要在官网https://www.jetbrains.com/idea/下载IntelliJ IDEA的最新版本并安装。 步骤2:新建Java项目 安装完成之后,启动Inte…

    Java 2023年5月26日
    00
  • Java_Spring之Spring 中的事务控制

    Java Spring之Spring 中的事务控制 在Java Spring中,事务控制是非常重要的一部分。本文将详细讲解Spring中的事务控制,包括事务的概念、事务的属性、事务的传播行为、事务的隔离级别和事务的示例说明。 事务的概念 事务是指一组操作,这些操作要么全部执行成功,要么全部执行失败。在Java Spring中,事务通常用于保证数据库操作的一致…

    Java 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部