一、什么是线程安全
线程安全是多线程编程时的计算机程序代码中的一个概念。. 在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
同一个资源,多个线程操作它的状态,会发生结果和期望不一致的情况.
二、举例
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>