赞
踩
目录
在Java中,线程安全是十分重要的。因为我们要让代码编写出来后锁产生的结果与我们的想法是一致的。所以本篇文章就为大家讲解一下线程安全的相关内容。
如果多线程环境下代码运行的结果是符合我们的预期的,即在单线程环境下应该获得的结果,则说这个程序是线程安全的。
1.线程的调度是随机的,抢占式执行
2.当前代码,多个线程同时修改一个变量
3.线程对于变量的修改不是“原子”的
4.内存可见性问题
5.指令重排序问题(4,5之后会学习到,这里就暂且不讨论)
在程序运行的过程中,如果不加其他代码,计算机是不会知道哪个线程先进行,哪个后进行,执行的顺序是随机的,这就是抢占式执行。
对于一个变量例如count,线程1对其++,线程2也对其++,这样会导致返回值出错
- public class Demo6 {
- private volatile static int count=0;
-
- public static void main(String[] args) throws InterruptedException {
- Thread t1=new Thread(() ->{
- for (int i = 0; i < 50000; i++) {
- count++;
- }
- });
-
- Thread t2=new Thread(()->{
- for (int i = 0; i < 50000; i++) {
- count++;
- }
- });
-
- t1.start();
- t2.start();
- t1.join();
- t2.join();
-
- System.out.println("count="+count);
- }
- }
按理说结果应该是100000,但执行后结果:
这就是多个线程改变同一个变量的结果,造成的线程不安全。
对于n++其实是三步操作:
1.从内存把数据读到CPU
2.进行数据更新
3.将更新的数据写回到CPU
原子性就是说当一个线程在执行的过程中,其他线程不能中途插进来。
假设有以下三件事要做:
1.去楼下拿快递
2.去客厅接杯水
3.去楼下拿外卖
如果按照1-2-3执行,效率会变低,所以JVM可以优化此操作,变为1-3-2,少去一次楼下,这就是指令重排序。
synchronized的作用就是加锁,解锁
上面count++的例子,如果加上了锁,那么结果就是正确的。
- public class Demo6 {
- private volatile static int count=0;
-
- public static void main(String[] args) throws InterruptedException {
-
- Object object=new Object();
-
- Thread t1=new Thread(() ->{
- synchronized (object){
- for (int i = 0; i < 50000; i++) {
- count++;
- }
- }
- });
-
- Thread t2=new Thread(()->{
- synchronized (object){
- for (int i = 0; i < 50000; i++) {
- count++;
- }
- }
- });
-
- t1.start();
- t2.start();
- t1.join();
- t2.join();
-
- System.out.println("count="+count);
- }
- }
代码结果:
synchronized会起到互斥的效果,某个线程执行到某个对象的synchronized中时,其他线程如果也执行到同一个对象synchronized就会阻塞等待。
进入synchronized修饰的代码块,相当于加锁。
推出synchronized修饰的代码块,相当于解锁。
“阻塞等待”的定义:
注意:
假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B ⽐ C 先来的, 但是 B 不⼀定就能获取到锁, ⽽是和 C 重新竞争, 并不遵守先来后到的规则.
synchronized同步块对同一线程来说是可重入的。
不可重入是什么意思?
简单来说就是一个线程已经加上锁了,但是它又加上了同一个锁,它既释放锁,又要再一次加锁,就导致成为了死锁,但synchronized没有这样的问题,所以它是可重入锁。
- public class Demo7 {
- Object object=new Object();
-
- public void method(){
- synchronized (object){
-
- }
- }
- }
- public class Demo8 {
- public void method(){
- synchronized (this){
-
- }
- }
- }
- public class Demo9 {
- public synchronized void method(){
-
- }
- }
- public class Demo10 {
- public synchronized static void method(){
-
- }
- }
Java 标准库中很多都是线程不安全的. 这些类可能会涉及到多线程修改共享数据, ⼜没有任何加锁措施。
不安全 如:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StirngBuilder
安全 如:Vector,HashTable,ConcurrentHashMap,StringBuffer(都涉及带锁操作)
虽然存在没有加锁的,但是不涉及“修改”,仍然是线程安全的:
String
本篇文章简单地给大家介绍了线程安全的意义,线程不安全的原因,以及其中的一种解决方法,欢迎大家在评论区讨论,感谢大家观看!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。