汉中商城网站建设,外贸网站建站多少钱,中国机械网招聘信息,app网站制作要多少费用多线程基础#xff1a;线程创建、同步与通信——学习指南 文章目录 多线程基础#xff1a;线程创建、同步与通信——学习指南前言一、线程创建1、原理2、案例3、使用场景推荐 二、线程同步1、原理2、案例1#xff09;synchronized关键字1.1#xff09;修饰实例方法#xf…多线程基础线程创建、同步与通信——学习指南 文章目录 多线程基础线程创建、同步与通信——学习指南前言一、线程创建1、原理2、案例3、使用场景推荐 二、线程同步1、原理2、案例1synchronized关键字1.1修饰实例方法当synchronized修饰一个实例方法时它锁定的是调用该方法的对象实例。1.2修饰静态方法当synchronized修饰一个静态方法时它锁定的是该方法所属的类对象。1.3修饰代码块synchronized也可以用来修饰一个代码块此时需要指定一个对象作为锁对象。 2ReentrantLock 3、对比 三、线程通信1、原理2、案例3、使用场景推荐 总结 前言
随着计算机技术的不断发展多线程编程已经成为现代软件开发中不可或缺的一部分。多线程能够充分利用多核CPU的并行计算能力提高程序的执行效率。对于初学者来说掌握多线程的基础知识是迈向高级编程的重要一步。
本文将详细介绍线程的创建、同步与通信包括怎么创建有哪几种创建方式怎么使用优缺点等等内容。 一、线程创建
1、原理
线程是操作系统进行运算调度的最小单位它被包含在进程之中是进程中的实际运作单位。Java中创建线程主要方式有继承Thread类、实现Runnable接口、实现Callable接口和线程池。本文先省略线程池。
2、案例
继承Thread类
继承Thread类创建线程示例通过继承java.lang.Thread类重写其run()方法然后创建子类对象并调用start()方法启动线程。
public class MyThread extends Thread { Override public void run() { System.out.println(MyThread is running.); // 线程执行的代码 } public static void main(String[] args) { MyThread thread new MyThread(); thread.start(); // 启动线程 // 直接new然后重写其中的run方法 new Thread(()-{//重写run方法log.info(run方法打印);}).start();}
}实现Runnable接口
实现Runnable接口创建线程示例实现java.lang.Runnable接口的类也可以被用来创建线程。这种方式的好处是一个类可以继承其他类并实现Runnable接口从而避免Java单继承的限制。
public class MyRunnable implements Runnable { Override public void run() { System.out.println(MyRunnable is running.); // 线程执行的代码 } public static void main(String[] args) { Thread thread new Thread(new MyRunnable()); thread.start(); // 启动线程 }
}实现Callable接口
实现Callable接口创建线程示例java.util.concurrent.Callable接口与Runnable类似但Callable可以返回执行结果并且可以抛出异常。通常与Future结合使用以获取异步计算的结果。
import java.util.concurrent.*; public class MyCallable implements CallableString { Override public String call() throws Exception { // 执行一些操作并返回结果 return Result of MyCallable; } public static void main(String[] args) throws ExecutionException, InterruptedException { //结合Future使用ExecutorService executor Executors.newSingleThreadExecutor(); FutureString future executor.submit(new MyCallable()); // 获取计算结果 String result future.get(); // 阻塞直到计算完成 System.out.println(result); executor.shutdown(); // 关闭线程池 }
}3、使用场景推荐
继承Thread类适用于简单的线程任务且不需要与其他线程共享资源。实现Runnable接口当需要多个线程共享一个任务时或者类已经继承了其他类时使用Runnable更合适。实现Callable接口当需要线程执行后返回结果时或者需要处理可能抛出的异常时使用Callable结合Future。
二、线程同步
1、原理
由于多个线程可能同时访问共享资源导致数据不一致或其他不可预测的问题因此需要进行线程同步。Java提供了多种同步机制如synchronized关键字、Lock接口及其实现类等。
2、案例
1synchronized关键字
synchronized是Java提供的一种内置锁机制它可以用来修饰方法或代码块。当一个线程进入一个对象的synchronized方法或代码块时它获得该对象的锁其他线程则无法进入该对象的synchronized方法或代码块直到锁被释放。使用synchronized关键字实现同步方法或同步代码块确保同一时间只有一个线程可以执行同步区域内的代码。
synchronized是非公平锁。这意味着多个线程在竞争synchronized锁时它们的获取顺序是不确定的不按照申请锁的顺序来排队。一个线程在等待锁时不管自己是不是在等待队列的头部都有机会在其他线程释放锁后立即获取锁。这种非公平性可能导致某些线程长时间无法获取到锁产生饥饿现象。然而synchronized锁也是可重入的即同一个线程可以反复获取锁多次然后需要释放多次。
1.1修饰实例方法当synchronized修饰一个实例方法时它锁定的是调用该方法的对象实例。
public class SynchronizedCounter { private int count 0; public synchronized void increment() { count; } public synchronized void decrement() { count--; } public synchronized int value() { return count; } public static void main(String[] args) { SynchronizedCounter counter new SynchronizedCounter(); // 假设有两个线程同时调用increment和decrement方法 // 由于increment和decrement方法都是synchronized的因此它们是线程安全的 // ... }
}
1.2修饰静态方法当synchronized修饰一个静态方法时它锁定的是该方法所属的类对象。
public class SynchronizedStaticCounter { private static int count 0; public static synchronized void increment() { count; } public static synchronized void decrement() { count--; } public static synchronized int value() { return count; } // ...
}1.3修饰代码块synchronized也可以用来修饰一个代码块此时需要指定一个对象作为锁对象。
public class SynchronizedBlock { private Object lock new Object(); private int count 0; public void increment() { synchronized (lock) { count; } } public void decrement() { synchronized (lock) { count--; } } // ...
}2ReentrantLock
ReentrantLock是Java中提供的一个可重入互斥锁它实现了Lock接口提供了比synchronized关键字更灵活、更强大的锁定机制。ReentrantLock通过显式地获取和释放锁来控制对共享资源的访问。
ReentrantLock的主要特性包括
公平性可以创建公平或非公平的锁。公平的锁按照请求锁的顺序来授予访问权限而非公平的锁则不保证这种顺序。可重入同一个线程可以多次获得同一个ReentrantLock实例而不会造成死锁。可中断当一个线程尝试获取一个由其他线程持有的锁时该线程可以选择等待或者响应中断。尝试锁定线程可以尝试获取锁如果锁不可用线程可以立即返回而不是阻塞。
示例
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private int count 0; private final ReentrantLock lock new ReentrantLock(); public void increment() { lock.lock(); // 获取锁 try { count; System.out.println(Incremented: count); } finally { lock.unlock(); // 释放锁 } } public void decrement() { lock.lock(); // 获取锁 try { count--; System.out.println(Decremented: count); } finally { lock.unlock(); // 释放锁 } } public static void main(String[] args) { ReentrantLockExample example new ReentrantLockExample(); // 创建两个线程一个增加计数一个减少计数 Thread incrementThread new Thread(() - { for (int i 0; i 5; i) { example.increment(); } }); Thread decrementThread new Thread(() - { for (int i 0; i 5; i) { example.decrement(); } }); // 启动线程 incrementThread.start(); decrementThread.start(); // 等待线程结束 try { incrementThread.join(); decrementThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }
}在上面的代码中increment()和decrement()方法都使用了ReentrantLock来控制对count变量的访问。在try块中执行临界区代码在finally块中确保锁被释放无论是否发生异常。
使用ReentrantLock时需要注意以下几点
一定要在finally块中释放锁确保锁不会因为异常而没有被释放从而导致死锁。避免在持有锁时执行耗时的操作以免其他线程长时间等待锁。考虑锁的粒度避免过度同步以减少线程间的竞争。
ReentrantLock还提供了其他高级功能比如条件变量通过newCondition()方法获取用于在多个线程之间实现更复杂的同步模式。这些高级功能使得ReentrantLock比synchronized关键字更加强大和灵活。
3、对比
使用场景 ReentrantLock适用于需要更精细控制同步的场景如中断等待获取锁、尝试非阻塞地获取锁或者与Condition结合使用实现复杂的线程间协调 synchronized适用于简单的同步场景如保护单个方法或代码块不被多个线程同时访问。灵活性 ReentrantLock提供了更灵活的锁定机制包括公平锁、可重入锁等特性以及更精细的锁控制方法。 synchronized相对固定自动管理锁的获取和释放无需显式操作。性能 性能因具体场景而异。在某些高并发场景下ReentrantLock可能通过更精细的锁策略提供更佳性能。但在一些简单场景下synchronized由于其内置性和JVM优化可能表现更好。易用性 ReentrantLock需要显式管理锁的获取和释放增加了代码复杂性和出错的可能性。 synchronized作为语言特性使用简单直观易于理解和维护。兼容性 synchronized作为Java的关键字与Java平台完全兼容无需额外依赖。 ReentrantLock是Java标准库的一部分但使用它需要显式导入相关类。锁的类型对比 synchronized是非公平锁。 ReentrantLock可以创建公平锁和非公平锁。
在大多数情况下synchronized是一个很好的选择因为它简单、易用且性能通常足够。但在需要更精细控制同步的场景下可以考虑使用ReentrantLock。
三、线程通信
1、原理
线程通信是指多个线程之间通过共享变量或其他机制进行信息交换和协作。wait()、notify()和notifyAll()是Java对象中的方法它们必须与synchronized一起使用用于在线程间通信。
2、案例
wait()使当前线程等待进入等待队列直到其他线程调用该对象的notify()或notifyAll()方法。notify()唤醒在此对象监视器上等待的单个线程。notifyAll()唤醒在此对象监视器上等待的所有线程。
public class WaitNotifyExample { private int count 0; private final Object lock new Object(); public void increment() { synchronized (lock) { while (count 5) { try { lock.wait(); // 当前线程等待直到被唤醒 } catch (InterruptedException e) { e.printStackTrace(); } } count; System.out.println(Incremented: count); lock.notifyAll(); // 唤醒所有在此对象上等待的线程 } } public void decrement() { synchronized (lock) { while (count 0) { try { lock.wait(); // 当前线程等待直到被唤醒 } catch (InterruptedException e) { e.printStackTrace(); } } count--; System.out.println(Decremented: count); lock.notifyAll(); // 唤醒所有在此对象上等待的线程 } } // ...
}这个示例展示了如何使用wait()和notifyAll()实现一个基本的生产者-消费者问题其中increment()方法类似于生产者增加计数而decrement()方法类似于消费者减少计数。
3、使用场景推荐
当需要在多个线程之间实现协同工作时可以使用线程通信机制。例如生产者消费者模型中生产者线程生产数据后唤醒消费者线程进行消费消费者线程消费完数据后唤醒生产者线程继续生产。代码示例如第2点所示 总结
多线程编程是Java编程中的重要部分掌握线程创建、同步与通信是成为高级程序员的关键。通过本文的介绍相信你们已经对多线程的基础有了初步的了解。在实际开发中还需要结合具体需求选择合适的线程创建方式、同步机制和通信方式以实现高效、稳定的多线程程序。
需要注意的是多线程编程涉及线程同步、数据共享和通信等复杂问题。在编写多线程程序时务必谨慎处理这些问题以避免出现竞态条件、死锁等线程安全问题。