带你快速搞定java多线程(3)

当我们需要处理一些比较消耗时间的操作时,多线程可以提高程序的执行效率,因此实现多线程在Java编程中也显得尤为重要。本文将带你从多方面快速搞定Java多线程,实现多任务并发执行。

1. 创建线程的三种方式

在Java中,创建线程的方式有三种:继承Thread类、实现Runnable接口以及使用线程池。

1.1 继承Thread类

继承Thread类是最简单的创建一个线程的方式,只需要重载Thread的run()方法即可。

class MyThread extends Thread {
    public void run() {
        // 线程所需要执行的代码
    }
}

1.2 实现Runnable接口

实现Runnable接口也是很常用的一种创建线程的方式。这种方式可以让一个类实现多个接口,同时也可以继承其他类。创建线程的时候,将实现Runnable接口的类传入Thread类的构造函数中即可。

class MyRunnable implements Runnable {
    public void run() {
        // 线程所需要执行的代码
    }
}

MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();

1.3 使用线程池

使用线程池是一种比较高级的创建线程的方式,它能够控制线程的数量,以及复用那些已经执行完毕的线程,从而提高程序的效率。Java中使用线程池需要借助java.util.concurrent包中的Executor和ExecutorService接口。

ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
    MyRunnable r = new MyRunnable();  // MyRunnable是实现了Runnable接口的类
    pool.execute(r);  // 将任务提交给线程池中的线程
}

2. 线程同步

当多个线程同时访问一个共享资源时,就可能会出现数据不一致的问题。为了避免这样的情况发生,就需要使用线程同步技术。Java中提供了synchronized关键字来保证同步。

2.1 synchronized关键字

synchronized关键字可以修饰一个方法或者一个代码块,只有拥有锁的线程才能够执行该方法或者代码块。其他线程只能在锁被释放后再去执行。

class Counter {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
    public synchronized void decrement() {
        count--;
    }
}

2.2 Lock接口

除了synchronized关键字,Java中还提供了Lock接口来实现线程同步。Lock接口提供了更细粒度的锁控制。

class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        try {
            lock.lock();  // 获取锁
            count++;
        } finally {
            lock.unlock();  // 释放锁
        }
    }

    public void decrement() {
        try {
            lock.lock();  // 获取锁
            count--;
        } finally {
            lock.unlock();  // 释放锁
        }
    }
}

示例说明

示例1

有一个系统需要对一组数进行排序,并将排序结果输出到控制台。这个过程比较耗时,需要使用多线程来提高效率。以下是实现该功能的代码:

class SortThread extends Thread {
    private int[] arr;

    public SortThread(int[] arr) {
        this.arr = arr;
    }

    public void run() {
        Arrays.sort(arr);
    }

    public int[] getArr() {
        return arr;
    }
}

public class SortTest {
    public static void main(String[] args) throws InterruptedException {
        int[] arr = {1, 5, 3, 7, 6, 2, 4, 9, 8, 10};
        SortThread t1 = new SortThread(Arrays.copyOfRange(arr, 0, 5));
        SortThread t2 = new SortThread(Arrays.copyOfRange(arr, 5, 10));
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        arr = mergeArray(t1.getArr(), t2.getArr());
        System.out.println(Arrays.toString(arr));
    }

    private static int[] mergeArray(int[] arr1, int[] arr2) {
        int[] arr = new int[arr1.length + arr2.length];
        int i = 0, j = 0, k = 0;
        while (i < arr1.length && j < arr2.length) {
            if (arr1[i] < arr2[j]) {
                arr[k++] = arr1[i++];
            } else {
                arr[k++] = arr2[j++];
            }
        }
        while (i < arr1.length) {
            arr[k++] = arr1[i++];
        }
        while (j < arr2.length) {
            arr[k++] = arr2[j++];
        }
        return arr;
    }
}

在上述代码中,我们创建了两个SortThread线程对象,分别对数组arr的前五个元素和后五个元素进行排序。这两个线程是同时启动的,所以整个过程可以得到充分的利用。当两个线程都执行完毕后,我们再将它们排序的结果进行合并。

示例2

一个生产者消费者模型,大致流程是:生产者不断生产产品,放入缓冲区;消费者不断从缓冲区取出产品进行消费。一旦缓冲区满了,生产者会暂停生产,等待消费者消费后再继续生产;一旦缓冲区为空,消费者会暂停消费,等待生产者生产产品后再继续消费。以下是实现该模型的代码:

class Clerk {
    private int productCount = 0;
    private final int MAX_PRODUCT = 20;

    public synchronized void addProduct() {
        if (productCount >= MAX_PRODUCT) {
            try {
                wait();  // 缓冲区已满,线程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        productCount++;
        System.out.println(Thread.currentThread().getName() + " 生产了一件产品,总共有 " + productCount + " 件产品");
        notifyAll();  // 唤醒所有等待的线程
    }

    public synchronized void removeProduct() {
        if (productCount <= 0) {
            try {
                wait();  // 缓冲区为空,线程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        productCount--;
        System.out.println(Thread.currentThread().getName() + " 消费了一件产品,总共有 " + productCount + " 件产品");
        notifyAll();  // 唤醒所有等待的线程
    }
}

class Producer implements Runnable {
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);  // 模拟生产过程耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.addProduct();
        }
    }
}

class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(2000);  // 模拟消费过程耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.removeProduct();
        }
    }
}

public class ProducerConsumerTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);
        new Thread(producer, "生产者1").start();
        new Thread(consumer, "消费者1").start();
        new Thread(producer, "生产者2").start();
        new Thread(consumer, "消费者2").start();
    }
}

在上述代码中,我们创建了一个Clerk类来模拟产品的生产和消费。这个类中包含了一个产品计数器和两个同步方法addProduct()和removeProduct(),分别用来添加和移除产品。在每个方法中,都用synchronized关键字修饰了方法,用来保证线程同步。

创建了两个线程生产者和消费者,分别在run()方法中对应调用Clerk类中的addProduct()和removeProduct()方法来生产和消费产品。这个过程中,使用了wait()和notifyAll()方法来控制线程的等待和唤醒。当缓冲区满或为空的时候,线程会进入等待状态并且释放锁,等待notifyAll()方法的唤醒。当notifyAll()方法被调用的时候,它会唤醒所有等待的线程,但是只有一个线程可以获得锁,继续执行下去。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:带你快速搞定java多线程(3) - Python技术站

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

相关文章

  • JAVA多线程的使用场景与注意事项总结

    我们来讲解“JAVA多线程的使用场景与注意事项总结”。首先,需要了解什么是多线程。多线程是指在同一时间内,处理不同任务的能力。在JAVA中,多线程基于线程对象(Thread对象)实现。 一、多线程的使用场景 多线程的使用场景包括以下几个方面: 1.1 处理耗时的操作 当需要处理耗时的操作时,比如进行网络IO操作、从磁盘读取数据、计算复杂数学函数等,使用多线程…

    多线程 2023年5月17日
    00
  • C语言细致讲解线程同步的集中方式

    C语言细致讲解线程同步的集中方式 本文将详细讲解C语言中实现线程同步的集中方式,并提供示例帮助读者更好地理解各种同步方式的实现原理。 关键术语解释 在讨论线程同步时,有几个术语是需要用到的,以下是这些术语的解释: 临界区:被多个线程同时访问、修改的共享资源所在的区域。 锁:用于在多个线程中协调对临界区访问的同步机制。 互斥操作:当一条线程进入临界区时,其他线…

    多线程 2023年5月16日
    00
  • java——多线程基础

    Java-多线程基础 什么是多线程 在程序中,一个线程就是一条执行路径。一个程序默认从主线程开始执行。如果程序中开辟了多个线程,则程序就会多个线程同时执行。 多线程可以大幅度提高程序的效率,因为多个线程可以同时执行,而不是一个一个依次执行。 多线程的实现 Java中实现多线程主要有两种方式,一种是继承Thread类,一种是实现Runnable接口。 继承Th…

    多线程 2023年5月17日
    00
  • C语言线程对象和线程存储的实现

    C语言线程对象和线程存储的实现涉及到操作系统底层的多线程机制,一般情况下需要用到系统提供的线程库来实现。下面将从以下三个方面介绍C语言线程对象和线程存储的实现。 线程对象的实现 线程对象是描述线程的实体,跟进程一样,线程对象通常包含线程ID、状态、执行栈等信息。在Unix/Linux系统中,线程对象可以用pthread_t结构体来表示,Windows系统中,…

    多线程 2023年5月16日
    00
  • Java并发编程之代码实现两玩家交换装备

    Java并发编程之代码实现两玩家交换装备攻略 本攻略将介绍如何使用Java并发编程实现两个玩家之间交换装备的操作。 一、问题描述 假设有两个玩家(Player A和Player B),每个玩家都有自己的背包(Bag),背包里面存放着各自的装备(Equipment)。现在Player A想要将自己的某个装备和Player B的某个装备互换。 二、解决方案 为了…

    多线程 2023年5月17日
    00
  • Java并发编程之Fork/Join框架的理解

    Java并发编程之Fork/Join框架的理解 什么是Fork/Join框架? Fork/Join框架是Java7引入的一种并行执行任务的机制,它通过将一个大任务分割成若干个小任务来并行地执行这些小任务,最终把这些小任务的结果合并起来得到大任务的结果。这种方式可以充分利用多核处理器的性能,加速任务执行速度,是一种高效的多线程编程方式。 Fork/Join框架…

    多线程 2023年5月16日
    00
  • Apache限制IP并发数和流量控制的方法

    当网站访问量较大时,为了防止某些IP用户访问过于频繁占用服务器资源,或者避免流量峰值对服务器的影响,我们可以通过限制IP并发数和流量控制来保障服务器的稳定性。下面是关于如何使用Apache来实现这两个目标的攻略。 限制IP并发数 步骤1:安装mod_evasive模块 首先,需要安装Apache的mod_evasive模块。在Linux系统中,可以直接通过以…

    多线程 2023年5月16日
    00
  • java如何实现多线程的顺序执行

    Java的多线程机制允许我们在应用中创建并行执行的多个线程,但有时候我们需要控制线程的执行顺序以实现特定的业务逻辑需求。这里提供两种实现多线程的顺序执行的方式: 1. 使用join()方法实现多线程顺序执行 Java中创建线程后,通过start()方法启动线程。当线程调用start()方法后,线程进入就绪状态并等待分配CPU时间片,但我们不能保证哪个线程会先…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部