【学习笔记】线程原子性-锁 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、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。


目录
相关文章
|
8天前
|
安全 Java 编译器
线程安全问题和锁
本文详细介绍了线程的状态及其转换,包括新建、就绪、等待、超时等待、阻塞和终止状态,并通过示例说明了各状态的特点。接着,文章深入探讨了线程安全问题,分析了多线程环境下变量修改引发的数据异常,并通过使用 `synchronized` 关键字和 `volatile` 解决内存可见性问题。最后,文章讲解了锁的概念,包括同步代码块、同步方法以及 `Lock` 接口,并讨论了死锁现象及其产生的原因与解决方案。
35 10
线程安全问题和锁
|
19天前
|
Java 开发者 C++
Java多线程同步大揭秘:synchronized与Lock的终极对决!
Java多线程同步大揭秘:synchronized与Lock的终极对决!
53 5
|
3天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
19天前
|
安全 Java 开发者
Java多线程同步:synchronized与Lock的“爱恨情仇”!
Java多线程同步:synchronized与Lock的“爱恨情仇”!
77 5
|
19天前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
18 2
|
19天前
|
Java 测试技术
Java多线程同步实战:从synchronized到Lock的进化之路!
Java多线程同步实战:从synchronized到Lock的进化之路!
78 1
|
14天前
|
安全 Java API
Java线程池原理与锁机制分析
综上所述,Java线程池和锁机制是并发编程中极其重要的两个部分。线程池主要用于管理线程的生命周期和执行并发任务,而锁机制则用于保障线程安全和防止数据的并发错误。它们深入地结合在一起,成为Java高效并发编程实践中的关键要素。
9 0
|
20天前
|
数据采集 Java Python
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
|
21天前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
46 1
|
4天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
24 15
一个Android App最少有几个线程?实现多线程的方式有哪些?