Java多线程详解

举报
没毛的刷子 发表于 2023/08/08 09:26:57 2023/08/08
【摘要】 线程是计算机中一种轻量级的进程,它是程序执行的基本单位,可以看做是进程中的一个小部分。 线程与进程不同,进程是操作系统进行资源分配和调度的基本单位,每个进程都有自己的独立内存空间和系统资源,进程之间相互独立。而线程是进程中的实际执行单位,一个进程中可以包含多个线程,这些线程共享同一份内存空间和资源。

 线程是什么?


线程是计算机中一种轻量级的进程,它是程序执行的基本单位,可以看做是进程中的一个小部分。

线程与进程不同,进程是操作系统进行资源分配和调度的基本单位,每个进程都有自己的独立内存空间和系统资源,进程之间相互独立。而线程是进程中的实际执行单位,一个进程中可以包含多个线程,这些线程共享同一份内存空间和资源。

一个线程拥有自己的ID、PC(程序计数器)、寄存器集合和栈区域等线程上下文(Thread Context),每个线程都有自己的状态,包括初始态、就绪态、运行态、阻塞态和死亡态。

在Java中,线程是由Thread类来实现的,也可以通过实现Runnable接口来创建线程,然后通过调用start()方法来启动线程的执行。线程执行完后会进入终止状态,不能再次启动执行。

线程的特点包括:

  • 线程是轻量级的执行单元,创建和销毁线程的开销很小。

  • 线程可以并发执行,可以利用多核CPU提高程序的执行效率。

  • 线程之间可以共享同一份数据空间,便于信息的共享和通信。

  • 线程的执行顺序和优先级可以通过调度算法来控制。

由于线程之间共享同一份内存空间和资源,因此在多线程编程中需要注意线程安全问题,如对共享变量的并发访问可能带来的数据竞争和死锁等问题。

线程的使用可以大大提高程序性能和执行效率,可以将任务并发执行从而缩短响应时间和提高效率,但也要注意避免过多创建线程带来的负面影响。同时,需要合理地利用锁、原子操作等技术来保证线程安全和正确性。

Java多线程基础


线程基础知识

  • 线程的概念:线程是操作系统能够进行运算调度的最小单位,它被包含在进程中,是进程中的实际运作单位。

  • 线程的状态:线程的状态包括新建状态、就绪状态、运行状态、阻塞状态和死亡状态。

  • 线程的创建:Java提供了三种方式来创建线程,分别是继承Thread类、实现Runnable接口和使用Executors框架。其中,实现Runnable接口是比较推荐的方式,因为Java只支持单继承,而且实现Runnable的方式更符合“高内聚低耦合”的设计思想。

  • 线程的同步:线程同步是指多个线程访问共享资源时的同步问题,Java提供了synchronized关键字和Lock接口来解决这一问题。

  • 线程的通信:线程通信是指多个线程之间的信息传递和协作,Java提供了wait()、notify()和notifyAll()等方法实现线程通信。

线程安全

线程安全是指多线程环境下程序始终能够正确地执行,不会出现数据不一致、死锁等问题。Java线程安全问题的产生通常涉及到以下几个方面:

  • 共享数据:多个线程访问同一块数据时容易出现线程安全问题。

  • 不可变数据:多个线程访问同一块数据时,如果数据是不可变的,则不会存在线程安全问题。

  • 线程操作:对于同一块数据,在同一时刻只能由一个线程进行操作,不能同时由多个线程操作。

Java提供了synchronized关键字用于解决线程同步问题,synchronized关键字可以保证同一时刻只有一个线程执行该代码块或方法。除此之外,Java还提供了Atomic包、Lock接口等方式来实现线程同步。

线程池

线程池是指在程序启动时预先创建若干个线程,并将这些线程存储在一个线程集合中,需要时直接从中获取线程来执行任务,执行完毕后再放回线程集合中。线程池的好处是可以避免频繁创建与销毁线程的开销,提高程序的性能。Java提供了ThreadPoolExecutor类来实现线程池,开发者可以根据自己的需求配置线程池的大小、线程存活时间、队列类型等参数。

Java多线程实现


继承Thread类方式

class MyThread extends Thread {

    @Override
    public void run() {
        // 线程执行的业务逻辑
    }
}

public class Main {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}

实现Runnable接口方式

class MyRunnable implements Runnable {

    @Override
    public void run() {
        // 线程执行的业务逻辑
    }
}

public class Main {
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

使用Lambda表达式方式

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            // 线程执行的业务逻辑
        });
        thread.start();
    }
}

线程同步

synchronized关键字

class MyObject {

    private int count;

    public synchronized void increase() {
        count++;
    }
}

public class Main {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myObject.increase();
                }
            }).start();
        }

        // 等待所有子线程执行完毕
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        System.out.println(myObject.getCount());
    }
}

Lock接口

class MyObject {

    private int count;

    private Lock lock = new ReentrantLock();

    public void increase() {
        try {
            lock.lock();
            count++;
        } finally {
            lock.unlock();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myObject.increase();
                }
            }).start();
        }

        // 等待所有子线程执行完毕
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        System.out.println(myObject.getCount());
    }
}

线程通信

wait()和notify()

class MyObject {

    private int count;

    public synchronized void produce() throws InterruptedException {
        while (count >= 10) {
            wait();
        }
        count++;
        System.out.println(Thread.currentThread().getName() + " produce " + count);
        notifyAll();
    }

    public synchronized void consume() throws InterruptedException {
        while (count <= 0) {
            wait();
        }
        count--;
        System.out.println(Thread.currentThread().getName() + " consume " + count);
        notifyAll();
    }
}

public class Main {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    myObject.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "producer").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    myObject.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "consumer").start();
    }
}

Condition接口

class MyObject {

    private int count;

    private Lock lock = new ReentrantLock();

    private Condition producerCondition = lock.newCondition();
    private Condition consumerCondition = lock.newCondition();

    public void produce() throws InterruptedException {
        try {
            lock.lock();
            while (count >= 10) {
                producerCondition.await();
            }
            count++;
            System.out.println(Thread.currentThread().getName() + " produce " + count);
            consumerCondition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void consume() throws InterruptedException {
        try {
            lock.lock();
            while (count <= 0) {
                consumerCondition.await();
            }
            count--;
            System.out.println(Thread.currentThread().getName() + " consume " + count);
            producerCondition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    myObject.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "producer").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    myObject.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "consumer").start();
    }
}

线程池

// 创建一个具有固定线程数(5个)的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);

// 向线程池提交一个任务(Runnable或Callable)
executor.submit(new Runnable() {
    @Override
    public void run() {
        // 线程具体执行的操作
    }
});

// 关闭线程池
executor.shutdown();

上面的代码中,通过调用Executors.newFixedThreadPool()方法创建一个具有固定线程数的线程池,然后通过executor.submit()方法向线程池提交一个任务,最后通过executor.shutdown()方法关闭线程池。

除了newFixedThreadPool()方法以外,还有其他常见类型的线程池,如:

  • newCachedThreadPool():一个可以动态调整线程数量的线程池,不限制线程数量,会根据需要自动创建和销毁线程,适用于处理大量短时间的任务。

  • newSingleThreadExecutor():一个只有一个工作线程的线程池,适用于需要保证任务按照指定顺序(如FIFO、LIFO、优先级)执行的场合。

线程池的做法可以避免频繁创建和销毁线程带来的性能开销,提高了线程的复用和管理效率,同时也可以避免线程数量过多导致资源耗尽的风险。根据实际的业务需求,可以选择合适的线程池类型和线程数量配置。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。