【学习笔记】线程原子性-锁 synchronized的用法(2)

简介: 【学习笔记】线程原子性-锁 synchronized的用法

修饰类

package com.lyy.concurrency.sync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedExample2 {
    // 修饰一个类
    public static void test1(int j){
        synchronized (SynchronizedExample2.class){
            for (int i = 0; i < 10; i++) {
                System.out.println("test1   j:"+j+" — i:"+i);
            }
        }
    }
    public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();//声明一个线程池
        //加上线程池相当于我们调用了两个线程  l
        //两个线程调用了同一个对象
        executorService.execute(() ->{
            example1.test1(1);
        });
        executorService.execute(() ->{
            example2.test1(2);
        });
    }
}


返回结果:

test1   j:1 — i:0
test1   j:1 — i:1
test1   j:1 — i:2
test1   j:1 — i:3
test1   j:1 — i:4
test1   j:1 — i:5
test1   j:1 — i:6
test1   j:1 — i:7
test1   j:1 — i:8
test1   j:1 — i:9
test1   j:2 — i:0
test1   j:2 — i:1
test1   j:2 — i:2
test1   j:2 — i:3
test1   j:2 — i:4
test1   j:2 — i:5
test1   j:2 — i:6
test1   j:2 — i:7
test1   j:2 — i:8
test1   j:2 — i:9

修饰静态方法:

package com.lyy.concurrency.sync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedExample2 {
    //修饰一个静态方法
    public static  synchronized void test2(int j){
        for (int i = 0; i < 10; i++) {
            System.out.println("test2 j:"+j+" — i:"+i);
        }
    }
    public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();//声明一个线程池
        //加上线程池相当于我们调用了两个线程  l
        //两个线程调用了同一个对象
        executorService.execute(() ->{
            example1.test2(1);
        });
        executorService.execute(() ->{
            example2.test2(2);
        });
    }
}

返回结果:

test2 j:1 — i:0
test2 j:1 — i:1
test2 j:1 — i:2
test2 j:1 — i:3
test2 j:1 — i:4
test2 j:1 — i:5
test2 j:1 — i:6
test2 j:1 — i:7
test2 j:1 — i:8
test2 j:1 — i:9
test2 j:2 — i:0
test2 j:2 — i:1
test2 j:2 — i:2
test2 j:2 — i:3
test2 j:2 — i:4
test2 j:2 — i:5
test2 j:2 — i:6
test2 j:2 — i:7
test2 j:2 — i:8
test2 j:2 — i:9

案例:

线程不安全案例:

package com.lyy.concurrency.example.count;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class CountExample1 {
    //请求总数
    public static int clientTotal = 5000;
//同时并发执行的线程数
    public static int threadTotal = 200;
    //
    public static int count = 0;
    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();//线程池
        final Semaphore semaphore = new Semaphore(threadTotal);//允许并发的数量
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
                executorService.execute(() ->{
                    try {
                        semaphore.acquire();//判断线程是否允许被执行
                        add();//当acquire()返回出来值之后才会被执行
                        semaphore.release();
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("count:"+count);
    }
    private static void add(){
        count++;
    }
}

执行结果:

count:4973
• 1

我们看到执行结果是4973,而正确的执行结果应该是5000,那么我们怎么才能让结果显示为5000呢,就看接下来我们使用synchronized实现一个线程安全的类

线程安全的类:

package com.lyy.concurrency.example.count;
import com.lyy.concurrency.annoatioons.NotThreadSafe;
import com.lyy.concurrency.annoatioons.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@ThreadSafe //线程安全的类
public class CountExample3 {
    //请求总数
    public static int clientTotal = 5000;
//同时并发执行的线程数
    public static int threadTotal = 200;
    //
    public static int count = 0;
    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();//线程池
        final Semaphore semaphore = new Semaphore(threadTotal);//允许并发的数量
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
                executorService.execute(() ->{
                    try {
                        semaphore.acquire();//判断线程是否允许被执行
                        add();//当acquire()返回出来值之后才会被执行
                        semaphore.release();
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("count:"+count);
    }
    private synchronized static void add(){
        count++;
    }
}

屏幕快照 2022-05-10 下午12.47.22.png


返回结果:


count:5000


总结:

1、synchronized:是不可中断锁,适合竞争不激烈,可读性比较好

2、无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;

3、如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

4、每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

5、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。


目录
相关文章
|
10天前
|
编解码 数据安全/隐私保护 计算机视觉
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
如何使用OpenCV进行同步和异步操作来打开海康摄像头,并提供了相关的代码示例。
31 1
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
|
11天前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
【10月更文挑战第6天】在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
18 2
|
18天前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
26 1
|
7天前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
19 0
|
10天前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
103 0
|
13天前
|
安全 调度 数据安全/隐私保护
iOS线程锁
iOS线程锁
22 0
|
16天前
|
Java 编译器 程序员
【多线程】synchronized原理
【多线程】synchronized原理
38 0
|
16天前
|
Java API
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
23 0
|
16天前
|
安全 Java 程序员
【多线程-从零开始-肆】线程安全、加锁和死锁
【多线程-从零开始-肆】线程安全、加锁和死锁
32 0
|
17天前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解