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日

相关文章

  • Java Apache POI报错“IllegalStateException”的原因与解决办法

    “IllegalArgumentException”是Java的Apache POI类库中的一个异常,通常由以下原因之一引起: 参数错误:如果参数不正确,则可能会出现此异常。例如,可能会尝试使用错误的参数调用方法。 以下是两个实例: 例1 如果参数不正确,则可以尝试使用正确的参数以解决此问题。例如,在Java中,可以使用以下代码: FileInputStre…

    Java 2023年5月5日
    00
  • Springboot与Maven多环境配置的解决方案

    下面我来详细讲解“Spring Boot与Maven多环境配置的解决方案”的完整攻略。 方案概述 在进行软件开发的过程中,不同的环境往往需要使用不同的配置,例如本地开发环境、测试环境、生产环境等。而Spring Boot作为一个快速开发的框架,在开发过程中需要使用到一些配置,例如数据库连接信息、端口号等。因此需要进行多环境配置的支持。 Maven是一个常用的…

    Java 2023年5月19日
    00
  • 简单了解Java位域的一些知识

    简单了解Java位域的一些知识 Java中的位域是一种内存优化技术,可以在一个变量中存储多个布尔值,以节省内存空间。本文将介绍Java位域的基本知识,包括如何使用位运算符来设置和获取位值,以及如何在Java中使用位域。 什么是Java位域? Java位域是一种数据结构,用于在单个变量中存储多个布尔值。它可以通过位运算符来实现。在Java的位域中,每个布尔值使…

    Java 2023年5月26日
    00
  • 常见的Java垃圾回收器有哪些?

    我们来详细讲解一下“常见的Java垃圾回收器有哪些?”这个问题的完整使用攻略。 问题背景 Java是一种垃圾自动回收语言,它通过垃圾回收器来自动管理内存。Java垃圾回收器根据内存使用情况,周期性地清理没有被引用的对象。Java垃圾回收器有多种不同的类型,每种类型都有其自身的特点和优劣势。 常见的Java垃圾回收器 Java垃圾回收器主要分为以下几种: Se…

    Java 2023年5月11日
    00
  • 图解Java经典算法折半查找的原理与实现

    这里为大家详细讲解“图解Java经典算法折半查找的原理与实现”的完整攻略。 什么是折半查找 折半查找(二分查找)是一种高效的查找算法,主要用于查找排好序的数组中是否存在某个元素。它的基本思想是将待查找区间不断划分为两个子区间,直到找到目标元素或者确定元素不存在为止。 折半查找的实现过程 以下为折半查找的详细实现过程。 1. 算法原理 首先,根据待查找元素与数…

    Java 2023年5月19日
    00
  • tomcat加载jar异常问题的分析与解决

    下面为大家讲解以下“tomcat加载jar异常问题的分析与解决”的完整攻略。 问题描述 在使用Tomcat启动项目时,可能会遇到以下异常情况: java.lang.NoClassDefFoundError: xxxxxxxxx 问题分析 这个异常通常表示,在Tomcat加载相关的jar包时,出现了问题。具体原因可能是以下几种情况之一: 项目中缺少相关的jar…

    Java 2023年5月19日
    00
  • Java实现对一行英文进行单词提取功能示例

    Java实现对一行英文进行单词提取功能 什么是单词提取功能? 在自然语言处理中,我们常常需要将一段英文分成若干个单词,这个过程被称为单词提取。在实际应用中,我们常常需要进行句子分析、文本分类和自然语言生成等任务,这些任务都离不开单词提取。 怎么实现单词提取? 在Java中,我们可以使用正则表达式实现单词的提取。下面是一段Java代码,展示了如何使用正则表达式…

    Java 2023年5月26日
    00
  • Java基础之Stream流原理与用法详解

    Java基础之Stream流原理与用法详解 1. 什么是Stream流? Stream流是Java 8中引入的一种新的API,它允许我们在集合上进行的函数式操作。它使我们能够以声明式方式处理集合中的元素,而不是直接以循环形式迭代它们。在Java 8之前,Collections类提供了大量用于操作集合的方法。但是,为了使用这些方法,你必须在代码中写出来循环,这…

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