Java线程同步的几种方法

1、使用关键字synchronized

  • 一个对象使用synchronized关键字声明,则只有一个执行线程可访问它,如果其他线程试图访问,这些线程将会被挂起,直到第一个拥有的的线程执行完

  • 当使用synchronized修饰一个对象的非静态方法时,当一个线程访问该方法时,其他线程不能访问该对象的其他被synchronized修饰的方法,但可以访问未被synchronized修饰的方法

  • 当使用synchronized修饰静态方法时,该方法同时只能被同一线程访问,但其他线程可访问该对象的其他非静态方法

2、使用wait()notify()notifyAll()

  • wait()方法可让线程挂起

  • notify()notifyAll()用于唤醒线程

  • 使用队列实现生产-消费者模型

    public class WaitAndNotify {
    
        /**
        * 模拟存储的队列.
        */
        @Data
        private class Storage {
            private int maxSize;
            private LinkedList<Integer> data;
    
            public Storage(int maxSize) {
                this.maxSize = maxSize;
                this.data = new LinkedList<>();
            }
    
            /**
            * 添加数据.
            */
            public synchronized void add(Integer data) {
                while (this.data.size() == maxSize) {
                    try {
                        System.out.println("队列已满");
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("添加数据");
                this.data.add(data);
                notifyAll();
            }
    
            /**
             * 获取数据
             */
            public synchronized Integer get() {
                while (this.data.size() == 0) {
                    try {
                        System.out.println("队列已空");
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Integer result = this.data.poll();
                System.out.println("获取数据: " + result);
                notifyAll();
                return result;
            }
        }
    
        public void run() {
            Random random = new Random();
            Storage storage = new Storage(20);
            Thread producer = new Thread(() -> {
                for (int i = 0; i < 100; i++) {
                    int n = random.nextInt(100);
                    storage.add(n);
                }
            });
            Thread consumer = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                storage.get();
            }
            });
            producer.start();
            consumer.start();
        }
    
        public static void main(String[] args) {
            WaitAndNotify test = new WaitAndNotify();
            test.run();
        }
    
    }
    

3、使用Lock实现同步

Java除了使用synchronized实现同步代码块外,还提供了另一种同步代码块机制,这种机制基于Lock及其实现类(如:ReentrantLock)

  • Locksynchronized的区别

    • Lock更加灵活:使用synchronized关键字,只能在同一块synchronized块结构中获取和释放。而Lock可实现更复杂的临界结构,如获取和释放不再同一块结构中

    • Lock提供了更多的功能:如tryLock()

    • Lock接口允许读写分离操作,允许多个读线程和一个写线程

    • Lock的性能更好

4、使用读写锁实现同步数据访问

接口ReadWriteLock和其唯一实现类ReentrantReadWriteLock(该类有两个锁,分别为读操作锁和写操作锁)可实现读写锁

  • 读写锁示例

    public class ReadAndWriter {
    
        private int price1;
        private int price2;
    
        ReadWriteLock readWriteLock;
    
        public ReadAndWriter(int price1, int price2) {
            this.price1 = price1;
            this.price2 = price2;
            this.readWriteLock = new ReentrantReadWriteLock();
        }
    
        /**
        * 获取数据(使用读操作锁)
        * @return
        */
        public int getPrice1() {
            readWriteLock.readLock().lock();
            int price = this.price1;
            readWriteLock.readLock().unlock();
            return price;
        }
    
        /**
        * 获取数据(使用读操作锁)
        * @return
        */
        public int getPrice2() {
            readWriteLock.readLock().lock();
            int price = this.price2;
            readWriteLock.readLock().unlock();
            return price;
        }
    
        /**
        * 设置数据(使用写操作锁)
        * @param price1
        * @param price2
        */
        public void setPrices(int price1, int price2) {
            readWriteLock.writeLock().lock();
            this.price1 = price1;
            this.price2 = price2;
            readWriteLock.writeLock().unlock();
        }
    
        public static void main(String[] args) {
            ReadAndWriter readAndWriter = new ReadAndWriter(0, 1);
            Random random = new Random();
            new Thread(() -> {
                for (int i = 0; i < 3; i++) {
                    System.out.println("writer");
                    readAndWriter.setPrices(random.nextInt(10000), random.nextInt(10000));
                    System.out.println("modified");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 5; j++) {
                        System.out.println(Thread.currentThread().getName() + " : " + readAndWriter.getPrice1());
                        System.out.println(Thread.currentThread().getName() + " : " + readAndWriter.getPrice2());
                    }
                }).start();
            }
        }
    }
    

5、使用Condition

一个锁可能关联一个或多个条件,这些条件通过Condition接口声明,Condition接口提供了线程的挂起和唤醒机制

  • 与锁绑定的条件对象都是通过Lock接口声明的newCondition()方法创建的。

  • 在使用条件时,必须拥有绑定的锁,即,所有带条件的代码必须在调用Lock对象的lock()unlock()方法之间

  • 当线程调用条件await()时,会自动释放绑定的锁

  • 如果调用了条件await(),但没有调用他的signal(),这个线程将会永远休眠

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值