Java同步锁synchronized用法的最全总结

Java同步锁synchronized用法的最全总结

1. 什么是同步锁?

在Java多线程编程中,同步锁是一种用于控制多线程并发访问的手段。它可以确保同一时间只有一个线程可以执行一段代码,从而保证线程安全。

synchronized关键字就是Java中最常用的同步锁。通过在方法或代码块上添加synchronized关键字,可以将这些方法或代码块变为同步代码,从而保证同一时间只有一个线程可以执行。

2. synchronized关键字的用法

2.1 修饰方法

在方法前加上synchronized关键字,可以将这个方法变成同步方法。当一个线程进入该方法时,会自动获取该方法所在对象的锁,其他线程只能等待锁被释放后才能进入该方法。

示例代码:

public synchronized void syncMethod(){
    //需要同步的代码块
}

2.2 修饰代码块

synchronized关键字还可以用于修饰代码块,形式为synchronized(obj){},其中obj可以是对象的实例变量或者class对象。

当一个线程进入synchronized代码块时,会自动获取锁,其他线程只能等待锁被释放后才能进入。

示例代码:

public void syncBlock(){
    synchronized(this){
        //需要同步的代码块
    }
}

2.3 synchronized关键字的工作原理

Java中的每个对象都有一个锁,当一个线程要访问一个加了synchronized关键字的方法或者代码块时,它必须先获得该对象的锁。如果该锁已经被其他线程获得了,那么该线程就会等待,直到该锁被释放。

3. synchronized关键字的注意事项

3.1 范围过大会影响性能

使用synchronized关键字时,需要注意将同步代码块的范围尽可能缩小,以减少锁竞争,提高程序性能。

3.2 不能跨线程传递锁

一个线程上了锁之后,其他线程是不能使用该锁的。所以不能将一个线程获得的锁传递给其他线程使用。

4. synchronized的替代方法

synchronized关键字的性能相对较低,还可能出现死锁等问题。可以考虑使用Lock接口及其实现类来代替synchronized关键字。

Lock接口提供了更加灵活和高效的锁控制机制,不同的实现类有不同的特点,可以根据需要进行选择。

5. 示例说明

5.1 示例1:银行账户取钱案例

这是一个简单的多线程取钱案例,其中涉及到银行账户余额的增减操作。为了保证线程安全,需要使用同步锁。

public class Account {
    private double balance;// 账户余额

    // 存款操作
    public synchronized void deposit(double money) {
        balance += money;
    }

    // 取款操作
    public synchronized void withdraw(double money) {
        if(balance>=money) {
            balance -= money;
            System.out.println(Thread.currentThread().getName()+" 取钱 "+money+" 元,余额为:"+balance);
        }
        else {
            System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");
        }
    }
}

public class Test {
    public static void main(String[] args) {
        final Account account = new Account();//创建账户
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                public void run() {
                    for (int j = 0; j < 3; j++) {//每个线程取3次钱
                        account.withdraw(100);//每次取100元
                    }
                }
            }, "Thread-" + i).start();//启动线程
        }
    }
}

5.2 示例2:多线程下载文件案例

多线程下载文件时,需要对文件下载操作进行同步,以保证多个线程不会同时写入同一个文件。

public class DownloadTask implements Runnable {

    private String url;//文件地址
    private String fileName;//文件名

    public DownloadTask(String url, String fileName) {
        this.url = url;
        this.fileName = fileName;
    }

    public void run() {
        try {
            URLConnection conn = new URL(url).openConnection();
            int contentLength = conn.getContentLength();//获取文件总长度
            InputStream is = conn.getInputStream();//获取文件输入流

            RandomAccessFile file = new RandomAccessFile(fileName, "rw");//使用RandomAccessFile进行文件写入
            file.setLength(contentLength);//设置写入文件长度
            file.close();

            int threadNum = 5;//线程数
            int blockSize = (contentLength + threadNum - 1) / threadNum;//每个线程需要下载的文件块大小

            for (int i = 0; i < threadNum; i++) {
                int start = i * blockSize;//文件块的起始位置
                int end = (i == threadNum - 1) ? contentLength : start + blockSize - 1;//文件块的结束位置

                new Thread(new BlockDownloadTask(i, start, end, url, fileName)).start();//启动线程,进行文件块下载
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 文件下载任务
    class BlockDownloadTask implements Runnable {
        private int blockId;//文件块编号
        private int start;//文件块的起始位置
        private int end;//文件块的结束位置
        private String url;//文件地址
        private String fileName;//文件名

        public BlockDownloadTask(int blockId, int start, int end, String url, String fileName) {
            this.blockId = blockId;
            this.start = start;
            this.end = end;
            this.url = url;
            this.fileName = fileName;
        }

        public void run() {
            try {
                File file = new File(fileName);//写入文件
                RandomAccessFile raf = new RandomAccessFile(file, "rw");//使用RandomAccessFile进行文件写入
                raf.seek(start);//指定写入位置

                URLConnection conn = new URL(url).openConnection();
                conn.setRequestProperty("Range", "bytes=" + start + "-" + end);//设置下载的文件块区间

                InputStream is = conn.getInputStream();//获取文件输入流
                byte[] buffer = new byte[1024];
                int len;
                while ((len = is.read(buffer)) != -1) {//读取输入流的数据,并写入文件
                    raf.write(buffer, 0, len);
                }

                raf.close();//关闭文件
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        String url = "http://www.example.com/bigfile.zip";//文件地址
        String fileName = "bigfile.zip";//文件名

        new Thread(new DownloadTask(url, fileName)).start();//启动下载任务
    }
}

以上就是Java同步锁synchronized用法的最全总结和两个示例说明的详细攻略。

阅读剩余 76%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java同步锁synchronized用法的最全总结 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • Redis服务之高可用组件sentinel详解

    Redis服务之高可用组件sentinel详解 什么是Redis Sentinel? Redis Sentinel 是 Redis 官方提供的一种高可用性解决方案,它可以对 Redis 主从集群进行自动的故障检测和故障转移。 当 Redis 主节点出现故障时,Sentinel 可以自动地将其中一个从节点切换为新的主节点,继续处理客户端请求。这一过程的自动化可…

    多线程 2023年5月17日
    00
  • 浅谈java并发之计数器CountDownLatch

    浅谈 Java 并发之计数器 CountDownLatch 概述 在 Java 并发编程中,CountDownLatch 是一个常用的同步工具类,可以用于控制多个线程的执行顺序,也可以用于实现线程的等待。 CountDownLatch 底层是基于 AQS(AbstractQueuedSynchronizer)实现的同步器,它的主要思想是让等待线程休眠,直到计…

    多线程 2023年5月16日
    00
  • java多线程编程之使用runnable接口创建线程

    当我们进行Java编程时,经常会需要使用多线程编程。在Java多线程编程中,一种创建线程的方式是通过实现Runnable接口。本文将对该方法进行详细介绍。 什么是Runnable接口 Runnable接口是Java语言中一个重要的接口,用于创建多线程。它只有一个方法:run(),该方法是Java多线程编程中最重要的方法之一。 使用Runnable接口创建线程…

    多线程 2023年5月17日
    00
  • JavaScript多线程详解

    JavaScript 多线程详解 多线程的意义 JavaScript 是一门单线程语言,无法同时处理多个任务,因为它的执行环境只有一个。但是随着 CPU 核心数量越来越多,单线程的 JavaScript 也显得有些捉襟见肘了。 因此,为了更好地利用硬件资源,减少任务的等待时间,让用户获得更流畅的体验,JavaScript 也开始了多线程的探索。 多线程的意义…

    多线程 2023年5月17日
    00
  • 高并发下如何避免重复数据产生技巧

    如何避免重复数据产生,在高并发环境下是一个非常重要的问题,因为一旦出现重复数据,就会影响整个系统的正常运行,甚至可能导致严重的数据安全问题。下面是一些可以避免重复数据产生的技巧: 数据库级别的锁定机制 在高并发环境下,一个经典的问题是“在同一时刻是否可以有多个用户同时修改同一条数据?” 事实上,这是不可能的,因为如果多个用户同时修改同一条数据,就会出现数据不…

    多线程 2023年5月17日
    00
  • .net面向对象之多线程(Multithreading)及 多线程高级应用

    .NET面向对象之多线程(Multithreading) 多线程概念 多线程是在单个程序里同时执行多个不同的流程的方式。在传统的单线程模式下,一个程序只能按顺序逐一执行操作,即使某些操作可以同时进行,也只能一个接一个地执行。而使用多线程可以在同一进程内同时执行多个流程,以提高程序的效率和用户体验度。 多线程的优点 多线程使得程序流程更加灵活,能够简化程序的逻…

    多线程 2023年5月16日
    00
  • Redis锁完美解决高并发秒杀问题

    Redis锁完美解决高并发秒杀问题 什么是Redis锁 Redis是一种内存数据存储工具,最常用于高速缓存(即将缓存的数据存储在内存中,加速访问速度)。Redis锁就是通过Redis实现分布式锁的一种方式。在高并发环境下,为了防止多线程同时访问同一个资源,需要使用分布式锁来保证多进程或多线程没有竞争情况下对共享资源的并发操作。 Redis锁的实现原理 在分布…

    多线程 2023年5月17日
    00
  • Java多线程Thread类的使用详解

    Java多线程Thread类的使用详解 简介 Java 程序是单线程的,但是程序中很多场景需要同时处理多个任务,因此 Java 提供了多线程并发处理机制,可以快速有效地解决这个问题。Thread 类是 Java 多线程的核心类之一,在 Java 中创建新线程有两种方法,一种是继承 Thread 类,另一种是实现 Runnable 接口,在本文中将详细讲解 T…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部