Java 多线程同步与线程间通信

简介: Java 多线程同步与线程间通信

文章目录


前言

1、synchronized 关键字修饰方法或代码段,只保证临界数据是互斥访问的

2、volatile修饰变量

3、ReentrantLock可重入锁,实现数据的互斥访问

4、wait和notify,实现线程间的等待通知机制

5、ReentrantLock结合Condition接口,实现线程间的等待通知机制


前言


java多线程同步和通信的方法有如下几种:


1.synchronized关键字修饰方法或代码段,实现数据的互斥访问


2.volatile修饰变量,实现多线程环境下数据的同步


3.ReentrantLock可重入锁,实现数据的互斥访问


4.synchronized结合Object的wait和notify方法,实现线程间的等待通知机制


5.ReentrantLock结合Condition接口的await()和signal()方法,实现线程间的等待通知机制


1、synchronized 关键字修饰方法或代码段,只保证临界数据是互斥访问的


java的每个对象都有一个内置锁,当用synchronized关键字修饰时,线程会获取该对象的内置锁,其他线程没有获取该对象的内置锁就会进入阻塞状态。


对象锁: synchronized关键字修饰代码段时,需要传入一个对象,通过该对象的内置锁来实现代码块的同步。


synchronized关键字修饰方法时,会将实例化的java对象的内置锁传进去,通过该锁来实现代码块的同步。


类锁: synchronized关键字修饰静态方法,会锁住该类的Class对象,此时如果调用该静态方法,将会锁住整个类。


注意: 同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

package com.kgc;
/**
 * @author:Tokgo J
 * @date:2020/2/23
 * @aim:
 */
public class Demo1 {
    static class MyThread extends Thread{
        Bank bank;
        public MyThread(String 线程, Bank bank){
            this.bank=bank;
        }
        @Override
        public void run(){
            for(int i=0;i<10;i++){
                bank.save(100); // 多线程调用该同步方法
            }
        }
    }
    static class Bank{
        private int account = 100 ; // 临界数据
        public int getAccount(){
            return account;
        }
        // 同步方法
        public synchronized void save(int money){
            account+=money;
            System.out.println("当前线程:"+Thread.currentThread().getId()+"当前余额:"+getAccount());
        }
        public void save1(int money){
            //同步代码块
            synchronized (this){ // 获取当前对象锁
                account+=money;
                System.out.println("当前线程:"+Thread.currentThread().getId()+"当前余额:"+getAccount());
            }
        }
    }
    public static void main(String[] args) {
        Bank bank = new Bank();
        MyThread th1 = new MyThread("线程1",bank);
        th1.start();
        MyThread th2 = new MyThread("线程2",bank);
        th2.start();
    }
}


2、volatile修饰变量


使用 volatile 修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此可以保证在多线程环境下保证数据的一致性

class Bank {
        //需要同步的变量加上volatile
        private volatile int account = 100;
        public int getAccount() {
            return account;
        }
        //这里不再需要synchronized
        public void save(int money) {
            account += money;
        }

 

3、ReentrantLock可重入锁,实现数据的互斥访问


class Bank{
        private ReentrantLock lock = new ReentrantLock();
        private int account = 100;//临界区数据
        public int getAccount(){
            return account;
        }
        public void put(int money)
        {
            lock.lock();
            try {
                account+=money;
                System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void get(int money)
        {
            lock.lock();
            try {
                account-=money;
                System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{
                lock.unlock();
            }
        }
    }


4、wait和notify,实现线程间的等待通知机制


通常在synchronized修饰的代码块中使用wait、notify/notifyAll函数。


当wait()被执行的时,会释放当前所持有的锁,然后让出CPU,进入等待阻塞状态;


当notify/notifyAll()被执行时候,会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,因此最好在同步代码块最后执行notify / notifyAll。

//消费者线程类,每隔100ms消费一个产品
    class CustomerThread extends Thread{
        Bank bank;
        public CustomerThread(Bank bank) {
            this.bank = bank;
        }
        @Override
        public void run() {
            while(true){
                bank.get(1);
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //生产者线程类,每隔300ms生产一个产品
    class ProductorThread extends Thread{
        Bank bank;
        public ProductorThread(Bank bank) {
            this.bank = bank;
        }
        @Override
        public void run() {
            while(true){
                bank.put(1);
                try {
                    sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //数据缓冲区
    class Bank{
        private int account = 100;//临界区数据
        public int getAccount(){
            return account;
        }
        public void put(int money){
            synchronized(this) //获取当前对象锁
            {
                if(getAccount() >= 120){ //若数量大于120则阻塞当前线程,释放对象锁
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                account+=money;//生产一个产品
                System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
                this.notifyAll();//唤醒其他线程
            }
        }
        public void get(int money){
            synchronized(this)
            {
                if(getAccount() <= 0){ // 若数量小于等于0则阻塞当前线程,释放对象锁
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                account-=money; // 消费一个产品
                System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
                this.notifyAll();//唤醒其他线程
            }
        }
    }
    //主函数调用例子
    public static void main(String[] args)
    {
        Bank bank = new Bank();//创建一个缓冲区对象
        ProductorThread th1 = new ProductorThread(bank);//创建生产者线程1
        th1.start();
        ProductorThread th2 = new ProductorThread(bank);//创建生产者线程2
        th2.start();
        CustomerThread th3 = new CustomerThread(bank);//创建消费者线程1
        th3.start();
    }


5、ReentrantLock结合Condition接口,实现线程间的等待通知机制


 

class CustomerThread extends Thread{
        Bank bank;
        public CustomerThread(Bank bank) {
            this.bank = bank;
        }
        @Override
        public void run() {
            while(true){
                bank.get(1);
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class ProductorThread extends Thread{
        Bank bank;
        public ProductorThread(Bank bank) {
            this.bank = bank;
        }
        @Override
        public void run() {
            while(true){
                bank.put(1);
                try {
                    sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class Bank{
        private ReentrantLock lock = new ReentrantLock(); // 创建可重入锁
        private Condition notEmpty = lock.newCondition(); // 创建非空条件变量
        private Condition notFullCondition = lock.newCondition(); // 创建非满条件变量
        private int account = 100;//临界区数据
        public int getAccount(){
            return account;
        }
        public void put(int money){
            lock.lock();
            try {
                if(getAccount() >= 120){ //当数量已满时,等待非满条件
                    notFullCondition.await();
                }
                //进行生产
                account+=money;
                System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
                notEmpty.signal();// 非空条件释放信号
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{
                lock.unlock();
            }
        }
        public void get(int money){
            lock.lock();
            try {
                if(getAccount() <= 0){ //当数量为空时,等待非空条件
                    notEmpty.await();
                }
                // 进行消费
                account-=money;
                System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
                notFullCondition.signal();// 非满条件释放信号
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{
                lock.unlock();
            }
        }
    }
    public static void main(String[] args)
    {
        Bank bank = new Bank();
        ProductorThread th1 = new ProductorThread(bank);
        th1.start();
        CustomerThread th3 = new CustomerThread(bank);
        th3.start();
    }
目录
相关文章
|
2月前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
38 1
[Java]线程生命周期与线程通信
|
18天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
31 3
|
27天前
|
Java 调度
Java 线程同步的四种方式,最全详解,建议收藏!
本文详细解析了Java线程同步的四种方式:synchronized关键字、ReentrantLock、原子变量和ThreadLocal,通过实例代码和对比分析,帮助你深入理解线程同步机制。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 线程同步的四种方式,最全详解,建议收藏!
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
19 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
31 2
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
19 1
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
34 1
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
48 1
C++ 多线程之初识多线程
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
38 1