java——多线程

news/2025/2/5 21:57:08/

文章目录

  • Java 的并发基础知识
    • 1. 创建线程
    • 2. 同步方法和同步代码块
    • 3. 线程安全的容器
    • 4. volatile 关键字
    • 5. Lock 和 Condition 接口
  • Java 多线程编程的基本框架
    • 1. 创建和启动线程
    • 2. 线程的状态转换
    • 3. 线程安全
    • 4. 死锁
  • Java 并发编程的高级技术
    • 1. 线程池
    • 2. 并发集合
    • 3. 原子类
    • 4. 锁

在这里插入图片描述

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()); // NEWthread.start();System.out.println(thread.getState()); // RUNNABLEThread.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 接口来解决竞态条件问题。

  1. 避免死锁

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

  1. 谨慎使用 Thread.sleep()

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

  1. 使用 Executor 框架

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

  1. 使用 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. 避免锁竞争

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

  1. 减少上下文切换

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

  1. 谨慎使用 ThreadLocal

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

  1. 使用 ForkJoin 框架

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

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


http://www.ppmy.cn/news/555352.html

相关文章

Python 错误和异常

目录 8. 错误和异常 8.1. 句法错误 8.2. 异常 8.3. 异常的处理 8.4. 触发异常 8.5. 异常链 8.6. 用户自定义异常 8.7. 定义清理操作 8.8. 预定义的清理操作 8.9. Raising and Handling Multiple Unrelated Exceptions 8.10. Enriching Exceptions with Notes 8. 错误…

Java 编程技巧之数据结构

使用HashSet判断主键是否存在 HashSet 实现 Set 接口&#xff0c;由哈希表&#xff08;实际上是 HashMap &#xff09;实现&#xff0c;但不保证 set 的迭代顺序&#xff0c;并允许使用 null 元素。HashSet 的时间复杂度跟 HashMap 一致&#xff0c;如果没有哈希冲突则时间复杂…

036-Andrnalin.2

PEID查壳&#xff0c;发现是C程序 用VB反编译器查看&#xff0c;有一个click事件 发现很多VB逆向函数 总结&#xff1a; 算法&#xff1a;取name的每一个字符ascii进行相加&#xff0c;之后再与1234567890相乘&#xff0c;将其转为字符串&#xff0c;并将第4和第9个字符转…

完美解决win10不认移动硬盘和U盘的问题

win10经常不识别移动硬盘&#xff0c;重启之后才认&#xff0c;但是用了一段时间之后此问题又会出现&#xff0c;总不能动不动就重启吧。 网上找到一个百度贴吧介绍的方法&#xff1a;把移动硬盘的usb插入4分之3能解决这个问题。 这不是搞笑吗&#xff1f; 结果我一试&#xf…

硬盘容量不能全部识别问题

公司的raid卡坏了&#xff0c;买了一个新的raid卡&#xff0c;安装发现硬盘容量不能全部识别 解决方法&#xff1a;更新一下raid卡的固件 想把一个服务器重新做下raid&#xff0c;发现不管做什么raid&#xff0c;硬盘容量都不正常&#xff0c;这个服务器没做raid前还蛮正常的 …

计算机不能识别监控硬盘分区,win7系统无法识别硬盘分区的详细教程

win7系统使用久了&#xff0c;好多网友反馈说win7系统无法识别硬盘分区的问题&#xff0c;非常不方便。有什么办法可以永久解决win7系统无法识别硬盘分区的问题&#xff0c;面对win7系统无法识别硬盘分区到底该如何解决&#xff1f;其实只需要1、在桌面上右键单击“此电脑”&am…

win8计算机硬盘无响应,win8.1系统读不出移动硬盘的原因和解决方法

一位用户说把移动硬盘插入win8.1系统电脑中没反应&#xff0c;读不出移动硬盘怎么回事呢&#xff1f;这是由于不当关机操作&#xff0c;导致电脑重启后无法识别和读取移动硬盘。找到原因后&#xff0c;接下来教程小编和大家分享win8.1系统读不出移动硬盘的原因和解决方法。 具体…