java——多线程

简介: java——多线程

Java 的并发基础知识


了解什么是线程、进程、多线程并发等概念,掌握 Java 中的 synchronized 和 volatile 关键字以及 Lock 和 Condition 接口等重要的并发工具。


下面是 Java 中并发基础知识的代码示例:


1. 创建线程


Java 中有两种方式来创建线程,一种是继承 Thread 类,另一种是实现 Runnable 接口。下面是使用继承 Thread 类的方式来创建线程的示例代码:


public class MyThread extends Thread {
    public void run() {
        System.out.println("MyThread is running...");
    }
}
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

2. 同步方法和同步代码块


在多个线程同时访问共享资源时,需要对这些资源进行同步,以避免数据不一致或者数据污染等问题。Java 中提供了 synchronized 关键字来实现同步,可以用于修饰方法和代码块。下面是使用 synchronized 修饰方法和代码块的示例代码:


public class Counter {
    private int count = 0;
    // 使用 synchronized 修饰方法
    public synchronized void increment() {
        count++;
    }
    // 使用 synchronized 修饰代码块
    public void decrement() {
        synchronized (this) {
            count--;
        }
    }
    public int getCount() {
        return count;
    }
}

3. 线程安全的容器


Java 中提供了一些线程安全的容器类,比如 Vector、Hashtable 和 Collections.synchronizedXXX 等。这些容器类可以保证在多个线程同时访问容器时,不会出现数据不一致或者数据污染等问题。下面是使用 Vector 来实现线程安全的并发队列的示例代码:

public class ConcurrentQueue {
    private Vector<String> queue = new Vector<>();
    public void enqueue(String element) {
        queue.add(element);
    }
    public String dequeue() {
        if (queue.isEmpty()) {
            return null;
        } else {
            return queue.remove(0);
        }
    }
}

4. volatile 关键字


volatile 关键字用于标记变量为“易失性变量”,即该变量可能被多个线程同时修改和访问,从而确保多个线程之间对该变量的可见性。下面是使用 volatile 修饰变量的示例代码:


public class MyThread extends Thread {
    private volatile boolean running = true;
    public void run() {
        while (running) {
            // do something here
        }
    }
    public void shutdown() {
        running = false;
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
        // 让主线程休眠一段时间后,停止子线程
        Thread.sleep(1000);
        thread.shutdown();
    }
}

5. Lock 和 Condition 接口


Lock 和 Condition 接口是 Java 中提供的可重入锁和条件变量,用于实现更灵活的线程同步控制。下面是使用 Lock 和 Condition 接口来实现线程同步的示例代码:


public class Buffer {
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();
    private List<Integer> list = new ArrayList<>();
    private int capacity;
    public Buffer(int capacity) {
        this.capacity = capacity;
    }
    public void put(int element) throws InterruptedException {
        lock.lock();
        try {
            while (list.size() == capacity) {
                notFull.await();
            }
            list.add(element);
            notEmpty.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public int get() throws InterruptedException {
        lock.lock();
        try {
            while (list.size() == 0) {
                notEmpty.await();
            }
            int element = list.remove(0);
            notFull.signalAll();
            return element;
        } finally {
            lock.unlock();
        }
    }
}

以上是 Java 中并发基础知识的相关示例代码。需要注意的是,在实际开发中,多线程程序的正确性和稳定性非常重要。在编写多线程程序时,应该尽可能地避免竞态条件、死锁、饥饿和活锁等问题,并且合理地控制线程的数量和优先级,以保证多线程程序的正确运行。


此外,在实际开发中,可以使用一些辅助工具来帮助我们调试和排查多线程程序中的问题,比如 Java VisualVM、jstack、jmap、jcmd 等。


总之,学习 Java 的并发基础知识只是掌握多线程编程的第一步,需要不断地实践和探索,提高自己的多线程编程能力,才能写出高效、健壮的应用程序。


Java 多线程编程的基本框架


包括创建和启动线程、线程的状态转换、线程安全和死锁等基本概念。

下面是使用 Java 多线程编程基本框架的示例代码:


1. 创建和启动线程


Java 中有两种方式来创建线程,一种是继承 Thread 类,另一种是实现 Runnable 接口。下面是使用继承 Thread 类的方式来创建和启动线程的示例代码:

public class MyThread extends Thread {
    public void run() {
        System.out.println("MyThread is running...");
    }
}
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}


2. 线程的状态转换


Java 中的线程有以下几种状态,可以通过 getState() 方法获取线程的状态:


  • NEW:新建状态,表示线程已经被创建但还没有调用 start() 方法;
  • RUNNABLE:运行状态,表示线程正在执行或者等待 CPU 时间片;
  • BLOCKED:阻塞状态,表示线程正在等待某个事件发生,如输入输出操作或 synchronized 代码块的监视器锁;
  • WAITING:等待状态,表示线程正在等待另一个线程通知或唤醒它;
  • TIMED_WAITING:计时等待状态,表示线程正在执行 sleep()、wait(long) 或 join(long) 方法;
  • TERMINATED:死亡状态,表示线程已经完成了它的任务,或者发生了异常而导致线程终止。

下面是使用 getState() 方法获取线程状态的示例代码:

public class MyThread extends Thread {
    public void run() {
        System.out.println("MyThread is running...");
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        System.out.println(thread.getState()); // NEW
        thread.start();
        System.out.println(thread.getState()); // RUNNABLE
        Thread.sleep(1000);
        System.out.println(thread.getState()); // TERMINATED
    }
}

3. 线程安全


在多个线程同时访问共享资源时,可能会出现数据不一致或者数据污染等问题。Java 中提供了 synchronized 关键字来实现同步,可以用于修饰方法和代码块。使用 synchronized 可以确保多个线程不会同时执行同一个方法或代码块,从而保证了线程安全。下面是使用 synchronized 实现线程安全的计数器的示例代码:


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

4. 死锁


死锁是指两个或多个线程都在等待对方释放资源,永久阻塞的情况。在多线程编程中,为了避免死锁问题,应该尽量避免线程间的循环依赖和资源竞争。下面是模拟死锁的示例代码:


public class DeadLockDemo {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread1 acquired lock1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("Thread1 acquired lock2");
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread2 acquired lock2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("Thread2 acquired lock1");
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

以上是使用 Java 多线程编程基本框架的相关示例代码。需要注意的是在实际开发中,需要谨慎地使用多线程编程,特别是对于涉及到共享资源的场景。以下是一些注意事项:


  1. 避免竞态条件

竞态条件是指多个线程访问共享资源时,由于执行顺序不确定而导致结果不确定的情况。可以通过 synchronized 关键字或者 Lock 接口来解决竞态条件问题。


2.避免死锁

死锁是指两个或多个线程都在等待对方释放资源,永久阻塞的情况。可以通过避免循环依赖和资源竞争来避免死锁问题。


3.谨慎使用 Thread.sleep()

Thread.sleep() 方法会使当前线程休眠一段时间,但它并不会释放对象监视器锁。因此,在多线程编程中,应该谨慎使用 Thread.sleep() 方法,避免出现死锁等问题。


4.使用 Executor 框架

Executor 是一个用于管理和执行线程的框架,它提供了更加灵活的线程池机制和任务队列,能够有效地管理和调度线程,避免线程数量过多或过少的问题。


5.使用 volatile 关键字

volatile 关键字可以保证变量在多个线程之间的可见性和有序性,可以避免由于线程之间不同步而导致的数据不一致问题。但是,它并不能保证原子性和互斥性,需要慎重使用。


总之,在多线程编程中,需要特别注意线程安全、死锁、性能等问题,并采用合适的方式来解决这些问题,才能写出高效、健壮的应用程序。


Java 并发编程的高级技术


如线程池、线程同步、阻塞队列、Callable 和 Future 等。

Java 并发编程的高级技术包括线程池、并发集合、原子类和锁等。下面对这些技术进行详细解释,并给出相应的示例代码:


1. 线程池


线程池是一个用于管理和调度线程的工具,它可以有效地控制线程的数量和优先级,提高多线程程序的性能和稳定性。在 Java 中,线程池主要有以下几种类型:


  • FixedThreadPool:固定大小的线程池,适合处理执行时间较短的任务;
  • CachedThreadPool:无限大小的线程池,适合处理执行时间较长的任务;
  • ScheduledThreadPool:固定大小的线程池,适合周期性地执行任务;
  • SingleThreadExecutor:单个线程的线程池,适合按顺序执行任务。

下面是使用 FixedThreadPool 创建线程池并执行任务的示例代码:


public class MyTask implements Runnable {
    private String name;
    public MyTask(String name) {
        this.name = name;
    }
    public void run() {
        System.out.println(name + " is running...");
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 5; i++) {
            executor.execute(new MyTask("Task" + i));
        }
        executor.shutdown();
        while (!executor.isTerminated()) {
            Thread.sleep(1000);
        }
        System.out.println("All tasks have been executed.");
    }
}

2. 并发集合


并发集合是一种线程安全的容器,可以在多个线程同时访问时保证数据的一致性和正确性。Java 中提供了一些并发集合类,包括 ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentLinkedQueue 等。下面是使用 ConcurrentHashMap 实现线程安全的计数器的示例代码:

public class Counter {
    private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    public void increment(String key) {
        map.compute(key, (k, v) -> v == null ? 1 : v + 1);
    }
    public void decrement(String key) {
        map.compute(key, (k, v) -> v == null ? -1 : v - 1);
    }
    public int getCount(String key) {
        return map.getOrDefault(key, 0);
    }
}

3. 原子类


原子类是一种线程安全的、不可分割的操作,可以保证对数值型变量的读取和修改操作都是原子性的。

Java 中提供了一些原子类,包括 AtomicInteger、AtomicLong、AtomicBoolean 等。下面是使用 AtomicInteger 实现线程安全的计数器的示例代码:


public class Counter {
    private AtomicInteger count = new AtomicInteger(0);
    public void increment() {
        count.incrementAndGet();
    }
    public void decrement() {
        count.decrementAndGet();
    }
    public int getCount() {
        return count.get();
    }
}

4. 锁


锁是一种用于控制多个线程对共享资源的访问的机制,可以保证同一时间只有一个线程可以访问共享资源。Java 中提供了 synchronized 关键字和 Lock 接口两种实现锁的方式。下面是使用 synchronized 实现锁的示例代码:


public class Counter {
    private int count = 0;
    private Object lock = new Object();
    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
    public void decrement() {
        synchronized (lock) {
            count--;
        }
    }
    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

以上是 Java 并发编程的高级技术及相关示例代码。在实际开发中,需要根据具体需求选择合适的技术来提高程序的性能和稳定性,并注意线程安全、死锁等问题。以下是一些注意事项:


1.避免锁竞争

在多线程编程中,锁的性能占据了很大一部分,因此需要尽量避免锁竞争。可以使用无锁算法、分段锁、读写锁等方式来减少锁竞争。


2.减少上下文切换

上下文切换是指操作系统将当前执行状态保存下来,然后切换到另一个线程或进程的执行状态的过程,会消耗额外的时间和资源。为了减少上下文切换,可以采用合适的线程数量、调整任务大小等方式。


3.谨慎使用 ThreadLocal

ThreadLocal 是一种线程本地变量,每个线程都有自己的副本,可以避免多线程访问时出现数据共享和污染的问题。但是,如果使用不当,会导致内存泄露等问题。


4..使用 ForkJoin 框架

ForkJoin 是一个用于并行计算的框架,它采用工作窃取算法来解决任务负载不均衡的问题,可以很好地提高程序的并发性能。


总之,在 Java 并发编程的高级技术中,需要注意锁竞争、上下文切换、ThreadLocal 的使用和并发集合的选择等问题,采用合适的技术来解决这些问题。


相关文章
|
6天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
2天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
15 9
|
5天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
2天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
5天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
14 3
|
4天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
5天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
13 1
|
5天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
6天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
27 1
|
9天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####