java必学必会之线程(1)

yizhihongxing

Java必学必会之线程(1)

一、线程的基本概念

1.1 线程定义

线程是进程中的执行单元,是轻量级的进程,一个进程可以有多个线程。线程拥有自己的执行栈和局部变量,但同时也可以访问共享变量。

1.2 线程状态

线程在其生命周期中可以处于以下几种状态:

  • NEW:新创建的线程,尚未开始执行。
  • RUNNABLE:正在 Java 虚拟机中执行的线程。
  • BLOCKED:等待获取一个排它锁以便进入同步块/方法的线程。
  • WAITING:等待另一个线程通知调度器一个条件的线程。
  • TIMED_WAITING:有限时间等待另一个线程通知调度器一个条件的线程。
  • TERMINATED:已经执行完毕的线程。

1.3 创建线程的三种方式

Java 中创建线程的方法有以下三种:

1.3.1 实现 Runnable 接口

创建一个实现 Runnable 接口的类,重写 run() 方法,并将该类的实例作为 Thread 构造函数的参数传入。

public class MyRunnable implements Runnable {
    public void run() {
        // 执行代码
    }
}
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();

1.3.2 继承 Thread 类

创建一个继承 Thread 类的子类,重写 run() 方法,并通过该子类的实例启动线程。

public class MyThread extends Thread {
    public void run() {
        // 执行代码
    }
}
MyThread myThread = new MyThread();
myThread.start();

1.3.3 使用匿名内部类

通过创建匿名内部类,重写 run() 方法并启动线程。

Thread thread = new Thread(new Runnable() {
    public void run() {
        // 执行代码
    }
});
thread.start();

二、线程的基本操作

2.1 线程的启动

通过调用 start() 方法启动线程。不能通过调用 run() 方法启动线程,这样不会创建新的线程,而是在当前线程中直接执行 run() 方法。

class MyThread extends Thread {
    @Override
    public void run() {
        // 执行代码
    }
}
MyThread myThread = new MyThread();
myThread.start();

2.2 线程的休眠

通过调用 Thread.sleep(millis) 方法,让当前线程暂停执行指定的时间,单位是毫秒。该方法可能会抛出 InterruptedException 异常。

try {
    Thread.sleep(1000); // 线程休眠1秒
} catch (InterruptedException e) {
    e.printStackTrace();
}

2.3 线程的中断

可以通过调用 Thread.interrupt() 方法向目标线程发送中断信号,但目标线程并不一定会立即响应,可能仍会继续执行。

class MyThread extends Thread {
    @Override
    public void run() {
        while (!Thread.interrupted()) {
            // 执行代码
        }
    }
}
MyThread myThread = new MyThread();
myThread.start();
myThread.interrupt(); // 发送中断信号

2.4 线程的等待和通知

Java 中通过 Object 类的 wait()、notify() 和 notifyAll() 方法实现线程等待和通知。

2.4.1 wait() 方法

wait() 方法会使当前线程进入等待状态,并且会释放当前线程持有的对象锁,直到其他线程调用该对象的 notify() 方法或 notifyAll() 方法来唤醒该线程。调用 wait() 方法的前提是必须先获得对象锁,否则会抛出 IllegalMonitorStateException 异常。

synchronized (object) {
    while (condition) {
        try {
            object.wait(); // 线程等待
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.4.2 notify() 方法

notify() 方法会随机唤醒一个正在等待该对象锁的线程,使其继续执行。并且当前线程并不会立即释放锁,而是在退出 synchronized 代码块后才释放。

synchronized (object) {
    condition = true;
    object.notify(); // 唤醒一个正在等待该对象锁的线程
}

2.4.3 notifyAll() 方法

notifyAll() 方法会唤醒正在等待该对象锁的所有线程,使其继续执行。并且当前线程并不会立即释放锁,而是在退出 synchronized 代码块后才释放。

synchronized (object) {
    condition = true;
    object.notifyAll(); // 唤醒所有正在等待该对象锁的线程
}

三、示例

3.1 生产者-消费者模型

生产者-消费者模型是一种常见的并发模型,通过一个或多个线程生产数据,另一个或多个线程消费数据,通过队列或者缓存区来解耦生产者和消费者,从而实现并发处理数据。

下面的示例展示了简单的生产者-消费者模型,一个生产者线程不断生产数据,保存到缓存区,多个消费者线程从缓存区读取数据进行消费。其中,Cache 类是缓存区类,包括 put() 和 take() 两个方法,分别是生产和消费数据的方法。

import java.util.LinkedList;

class Cache<T> {
    private final LinkedList<T> queue = new LinkedList<>();
    private final int limit;

    public Cache(int limit) {
        this.limit = limit;
    }

    public synchronized void put(T t) throws InterruptedException {
        while (queue.size() == limit) {
            wait();
        }
        queue.add(t);
        notifyAll();
    }

    public synchronized T take() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }
        T t = queue.removeFirst();
        notifyAll();
        return t;
    }
}

class Producer extends Thread {
    private final Cache<Integer> cache;
    private final int value;

    public Producer(Cache<Integer> cache, int value) {
        this.cache = cache;
        this.value = value;
    }

    @Override
    public void run() {
        while (true) {
            try {
                cache.put(value);
                System.out.println("Producer: " + value);
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer extends Thread {
    private final Cache<Integer> cache;

    public Consumer(Cache<Integer> cache) {
        this.cache = cache;
    }

    @Override
    public void run() {
        while (true) {
            try {
                int value = cache.take();
                System.out.println("Consumer: " + value);
                sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Cache<Integer> cache = new Cache<>(10);
        Producer producer = new Producer(cache, 1);
        Consumer consumer1 = new Consumer(cache);
        Consumer consumer2 = new Consumer(cache);
        producer.start();
        consumer1.start();
        consumer2.start();
    }
}

3.2 多线程修改同一对象

多个线程修改同一个对象的属性或者成员变量时,可能会出现线程安全问题,如数据不一致、数据污染等问题。下面的示例模拟了一个共享变量 num 被多个线程读写的情况,其中线程 A 不断将 num 加一,线程 B 不断将 num 减一,最终结果应该是 num 的值不变。但是由于线程不安全,最终 num 的值可能不是预期的值。

class MyThread extends Thread {
    private static int num = 0;

    private final char operator;

    public MyThread(char operator) {
        this.operator = operator;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (operator == '+') {
                num++;
            } else if (operator == '-') {
                num--;
            }
        }
        System.out.println("operator: " + operator + ", num: " + num);
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new MyThread('+');
        Thread thread2 = new MyThread('-');
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("final num: " + MyThread.num);
    }
}

为了解决这个问题,我们可以使用 synchronized 关键字来保证任意时刻只有一个线程执行修改操作。

class MyThread extends Thread {
    private static int num = 0;

    private final char operator;

    public MyThread(char operator) {
        this.operator = operator;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synchronized (MyThread.class) {
                if (operator == '+') {
                    num++;
                } else if (operator == '-') {
                    num--;
                }
            }
        }
        System.out.println("operator: " + operator + ", num: " + num);
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new MyThread('+');
        Thread thread2 = new MyThread('-');
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("final num: " + MyThread.num);
    }
}

以上就是线程的基本概念、操作和两个示例的详细解释。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java必学必会之线程(1) - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 游戏内存如何炼成的 厂商工程师手记曝光

    游戏内存如何炼成的 厂商工程师手记曝光 简介 在这篇攻略中,我们将详细讲解游戏内存的制造过程。这些信息来自一位厂商工程师的手记,揭示了游戏内存的制造过程和一些关键细节。我们将介绍游戏内存的基本原理、制造流程以及两个示例说明。 游戏内存基本原理 游戏内存是计算机系统中的一种关键组件,用于存储正在运行的游戏程序和相关数据。它是一种易失性存储器,意味着在断电或重启…

    other 2023年8月1日
    00
  • Python爬虫包 BeautifulSoup 递归抓取实例详解

    Python爬虫包 BeautifulSoup 递归抓取实例详解 什么是BeautifulSoup? BeautifulSoup 是 Python 的一个 HTML 解析库,它可以自动解析 HTML 文档,并提供了许多简便的方法来处理 HTML 元素。它可以轻松地帮助我们快速提取出需要的信息,是一个强大的工具。 安装BeautifulSoup 使用pip可以…

    other 2023年6月27日
    00
  • dataframe删除第一列

    以下是使用Python中pandas库的DataFrame删除第一列的完整攻略,包含两个示例: 步骤1:导入pandas库 在Python中使用pandas库来操作DataFrame,首先需要导入pandas库。打开Python交互式环境或Python脚本,并输入以下命令: import pandas as pd 将导入pandas库将其命名为pd,以便在后…

    other 2023年5月6日
    00
  • c++virtualvoidvsnovirtual

    C++中virtual和非virtual函数的区别 在C++中,virtual和非virtual函数的区别在于是否支持多态。本文将详细讲解virtual和非virtual函数的区别,包括使用场景、实现方式、示例等内容。 virtual函数 在C++中,virtual函数是支持多态的。当一个类中的函数被声明为virtual时,可以被子类重写,从而实现多态。以下…

    other 2023年5月8日
    00
  • android网络权限配置

    以下是详细讲解“android网络权限配置的完整攻略”的标准Markdown格式文本,包含两个示例说明: Android网络权限配置的完整攻略 在Android应用程序中,如果需要使用网络功能,就需要配置网络权限。本攻略将介绍如何在Android应用程序中配置网络权限。 步骤一:在AndroidManifest.xml文件中添加网络 在Android用程序中…

    other 2023年5月10日
    00
  • PPT怎么利用触发器实现简单交互动画?

    下面是关于“PPT怎么利用触发器实现简单交互动画?”的完整攻略。 什么是触发器 在PPT中,触发器是一种可以触发特殊效果的工具,可以让幻灯片更生动、有趣。在PPT中,触发器可以让元素随着鼠标或其他用户操作而发生动画效果。触发器有各种各样的类型,比如按钮、文本框、图片等,可以实现不同的动画效果。 如何利用触发器实现简单交互动画 利用触发器实现简单交互动画的步骤…

    other 2023年6月27日
    00
  • jQuery实现的自动加载页面功能示例

    以下是 “jQuery实现的自动加载页面功能示例” 的完整攻略: 1. 什么是自动加载页面功能? 自动加载是指当用户向下滚动页面时,网站自动向用户加载下一段内容,从而实现更好的用户体验。在jQuery中,我们可以使用scroll事件来检测用户滚动,并通过AJAX技术从服务器上获取数据,然后将其插入页面中。 2. 使用jQuery实现自动加载页面的步骤 为了实…

    other 2023年6月25日
    00
  • iOS开发中class和#import的区别介绍

    当我们进行 iOS 开发时,使用 Objective-C 语言是非常常见的。在 Objective-C 语言中,有两个关键字 class 和 #import,在程序中起着很重要的作用。下面我将详细介绍二者之间的区别及其使用。 class Class 是 Objective-C 中的一个关键字,它用于定义一个类。在 Objective-C 中,所有的东西都被认…

    other 2023年6月26日
    00
合作推广
合作推广
分享本页
返回顶部