java线程之线程安全

简介: java线程之线程安全

一、什么是线程安全

线程安全是多线程编程时的计算机程序代码中的一个概念。. 在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

同一个资源,多个线程操作它的状态,会发生结果和期望不一致的情况.

二、举例

 
import lombok.Data;
 
@Data
public class UnsafeSwquence {
    private int value;
 
    public int getNest(){
        //value自增
        value++;
        //答应打印当前线程的名称及值
        System.out.println(Thread.currentThread().getName()+":"+value);
        return value;
    }
} 

1、单线程操作

结果总是100

 /**
     * 单线程
     */
    @Test
    void test01() {
        UnsafeSwquence unsafeSwquenc = new UnsafeSwquence();
        for (int i = 0; i < 100; i++) {
            unsafeSwquenc.getNest();
        }
        System.out.println(unsafeSwquenc.getValue());
    }
main:1
main:2
main:3
main:4
main:5
main:6
main:7
main:8
main:9
main:10
main:11
main:12
main:13
main:14
main:15
main:16
main:17
main:18
main:19
main:20
main:21
main:22
main:23
main:24
main:25
main:26
main:27
main:28
main:29
main:30
main:31
main:32
main:33
main:34
main:35
main:36
main:37
main:38
main:39
main:40
main:41
main:42
main:43
main:44
main:45
main:46
main:47
main:48
main:49
main:50
main:51
main:52
main:53
main:54
main:55
main:56
main:57
main:58
main:59
main:60
main:61
main:62
main:63
main:64
main:65
main:66
main:67
main:68
main:69
main:70
main:71
main:72
main:73
main:74
main:75
main:76
main:77
main:78
main:79
main:80
main:81
main:82
main:83
main:84
main:85
main:86
main:87
main:88
main:89
main:90
main:91
main:92
main:93
main:94
main:95
main:96
main:97
main:98
main:99
main:100
100

2、多线程操作

结果概率出现其他值

 /**
     * 多线程
     * @throws InterruptedException
     */
    @Test
    void test02() throws InterruptedException {
        UnsafeSwquence unsafeSwquence = new UnsafeSwquence();
        for (int i = 0; i < 100; i++) {
            //创建线程并启动
            new Thread(() -> {
                unsafeSwquence.getNest();
            }).start();
        }
        //等待线程自增结束
        Thread.sleep(1000);
        //打印自增结果
        System.out.println(unsafeSwquence.getValue());
    }
Thread-1:1
Thread-2:2
Thread-9:3
Thread-4:4
Thread-5:5
Thread-7:6
Thread-3:7
Thread-10:8
Thread-11:9
Thread-13:11
Thread-12:10
Thread-6:13
Thread-14:13
Thread-16:15
Thread-26:14
Thread-27:16
Thread-17:17
Thread-18:18
Thread-19:19
Thread-34:20
Thread-35:21
Thread-22:22
Thread-23:23
Thread-24:24
Thread-38:25
Thread-15:26
Thread-8:27
Thread-29:28
Thread-31:29
Thread-41:34
Thread-25:35
Thread-21:40
Thread-45:33
Thread-30:32
Thread-43:31
Thread-28:30
Thread-42:31
Thread-44:45
Thread-46:44
Thread-32:43
Thread-33:42
Thread-20:41
Thread-40:39
Thread-36:39
Thread-37:38
Thread-39:38
Thread-50:49
Thread-49:48
Thread-48:47
Thread-47:46
Thread-53:52
Thread-52:51
Thread-51:50
Thread-54:53
Thread-56:54
Thread-55:55
Thread-57:56
Thread-59:58
Thread-58:57
Thread-61:60
Thread-62:61
Thread-60:59
Thread-72:66
Thread-66:65
Thread-65:64
Thread-77:70
Thread-64:63
Thread-63:62
Thread-67:71
Thread-76:69
Thread-70:68
Thread-75:74
Thread-68:67
Thread-74:73
Thread-73:72
Thread-69:75
Thread-71:76
Thread-78:77
Thread-80:78
Thread-81:79
Thread-83:80
Thread-84:82
Thread-82:81
Thread-79:83
Thread-85:84
Thread-87:85
Thread-88:86
Thread-89:87
Thread-86:88
Thread-90:90
Thread-92:91
Thread-91:90
Thread-93:92
Thread-96:93
Thread-95:94
Thread-94:95
Thread-97:96
Thread-98:97
Thread-100:98
Thread-99:99
99

三、如何保证线程安全

java并发编程实战给的三种修复方式

1 不在线程之间共享改状态变量;
 
2 将状态变量修改为不可变的变量;
 
3 在访问状态变量时使用同步;

1、使用同步状态量,需改getNest方法,添加synchronized关键字(不推荐,性能不佳).

   public synchronized int getNest() {
        //value自增
        value++;
        //打印当前线程的名称及值
        System.out.println(Thread.currentThread().getName() + ":" + value);
        return value;
    }
Thread-1:1
Thread-2:2
Thread-9:3
Thread-4:4
Thread-6:5
Thread-5:6
Thread-7:7
Thread-8:8
Thread-10:9
Thread-12:10
Thread-3:11
Thread-11:12
Thread-14:13
Thread-13:14
Thread-16:15
Thread-17:16
Thread-29:17
Thread-30:18
Thread-20:19
Thread-33:20
Thread-22:21
Thread-23:22
Thread-24:23
Thread-25:24
Thread-26:25
Thread-15:26
Thread-27:27
Thread-28:28
Thread-44:29
Thread-45:30
Thread-19:31
Thread-18:32
Thread-37:33
Thread-48:34
Thread-38:35
Thread-49:36
Thread-39:37
Thread-40:38
Thread-32:39
Thread-41:40
Thread-53:41
Thread-42:42
Thread-54:43
Thread-43:44
Thread-46:45
Thread-47:46
Thread-52:47
Thread-31:48
Thread-51:49
Thread-57:50
Thread-50:51
Thread-58:52
Thread-34:53
Thread-21:54
Thread-59:55
Thread-60:56
Thread-36:57
Thread-35:58
Thread-63:59
Thread-62:60
Thread-61:61
Thread-56:62
Thread-55:63
Thread-65:64
Thread-64:65
Thread-67:66
Thread-66:67
Thread-68:68
Thread-69:69
Thread-70:70
Thread-71:71
Thread-72:72
Thread-74:73
Thread-73:74
Thread-75:75
Thread-76:76
Thread-77:77
Thread-78:78
Thread-79:79
Thread-81:80
Thread-80:81
Thread-82:82
Thread-83:83
Thread-84:84
Thread-85:85
Thread-86:86
Thread-87:87
Thread-88:88
Thread-89:89
Thread-91:90
Thread-90:91
Thread-92:92
Thread-93:93
Thread-94:94
Thread-95:95
Thread-96:96
Thread-97:97
Thread-98:98
Thread-99:99
Thread-100:100
100


2、使用原子类进行计数(推荐)

jdk提供的原子类

import java.util.concurrent.atomic.AtomicInteger;
 
@Data
public class UnsafeSwquence {
    private int value;
    private AtomicInteger num = new AtomicInteger(0);
 
    public synchronized int getNest() {
        //value自增
        value++;
        //打印当前线程的名称及值
        System.out.println(Thread.currentThread().getName() + ":" + value);
        return value;
    }
 
    public int getNestInteger() {
        //num
        num.incrementAndGet();
        //打印当前线程的名称及值
        System.out.println(Thread.currentThread().getName() + ":" + num.get());
        return num.get();
    }
}

测试

 /**
     * 多线程
     * @throws InterruptedException
     */
    @Test
    void test03() throws InterruptedException {
        UnsafeSwquence unsafeSwquence = new UnsafeSwquence();
        for (int i = 0; i < 100; i++) {
            //创建线程并启动
            new Thread(() -> {
                unsafeSwquence.getNestInteger();
            }).start();
        }
        //等待线程自增结束
        Thread.sleep(1000);
        //打印自增结果
        System.out.println(unsafeSwquence.getNum().get());
    }
Thread-1:1
Thread-2:2
Thread-3:3
Thread-4:4
Thread-5:5
Thread-6:6
Thread-7:7
Thread-8:8
Thread-9:9
Thread-10:10
Thread-11:11
Thread-13:12
Thread-12:13
Thread-14:14
Thread-15:15
Thread-16:16
Thread-17:17
Thread-18:18
Thread-19:19
Thread-20:20
Thread-21:21
Thread-22:22
Thread-23:23
Thread-24:24
Thread-25:25
Thread-27:26
Thread-26:27
Thread-29:28
Thread-31:29
Thread-30:30
Thread-28:31
Thread-33:32
Thread-32:33
Thread-35:34
Thread-34:35
Thread-37:36
Thread-39:37
Thread-36:38
Thread-40:39
Thread-38:40
Thread-42:41
Thread-41:42
Thread-44:43
Thread-43:44
Thread-46:45
Thread-47:46
Thread-45:47
Thread-48:48
Thread-50:49
Thread-49:50
Thread-51:51
Thread-52:52
Thread-53:53
Thread-54:54
Thread-55:55
Thread-56:56
Thread-57:57
Thread-58:58
Thread-59:59
Thread-60:60
Thread-61:61
Thread-62:62
Thread-63:63
Thread-64:64
Thread-65:65
Thread-66:66
Thread-67:67
Thread-68:68
Thread-69:69
Thread-70:70
Thread-71:71
Thread-72:72
Thread-73:73
Thread-74:74
Thread-76:75
Thread-75:76
Thread-77:77
Thread-79:78
Thread-78:79
Thread-81:80
Thread-82:81
Thread-80:82
Thread-83:83
Thread-84:84
Thread-85:85
Thread-86:86
Thread-87:87
Thread-88:88
Thread-89:89
Thread-90:90
Thread-91:91
Thread-93:92
Thread-94:93
Thread-95:94
Thread-92:95
Thread-96:96
Thread-97:97
Thread-98:98
Thread-100:99
Thread-99:100
100

示例用到的依赖

    <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
        </dependency>

相关文章
|
1天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
3天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
3天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
3天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
18 3
|
3天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
38 2
|
11天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
43 6
|
20天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
20天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
43 3
|
21天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
24天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
29 2