线程安全问题

目录

 

1、线程安全

2、内存模型JMM

3、多线程有三大特性

原子性

可见性

有序性

4、Volatile特性

5、ThreadLocal

ThreadLoca实现原理

6、线程安全解决办法

1)Lock锁

2)同步代码块synchronized

3)同步方法

①非静态同步方法使用的是this锁

②静态同步方法使用的是class锁

Lock与synchronized 关键字的区别


1、线程安全

       当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。

2、内存模型JMM

共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

3、多线程有三大特性

原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

一个很经典的例子就是银行账户转账问题:
比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。

可见性

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。

在项目中,只要是全局共享变量,一般都需要加上Volatile关键字

可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。

在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。

有序性

程序执行的顺序按照代码的先后顺序执行。

一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下:

int a = 10;    //语句1

int r = 2;    //语句2

a = a + 3;    //语句3

r = a*a;     //语句4

则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4
但绝不可能 2-1-4-3,因为这打破了依赖关系。
显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

4、Volatile特性

      1.保证此变量对所有的线程的可见性,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存来完成。

  2.禁止指令重排序优化。如下:

       volatile int a = 10; 

       int r = 2;    //语句1

       r = 4;        //语句2

       a = a + 3;    //语句3

       r = 2*2;     //语句4

语句1、2一定在语句3前执行,而语句4一定在语句3后执行。

volatile 性能:

  volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

5、ThreadLocal

ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量。

 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

class Res {
	public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
		protected Integer initialValue() {
			return 0;
		}
	};
	public Integer getNumber() {
		int count = threadLocal.get() + 1;
		threadLocal.set(count);
		return count;
	}
}

public class Test extends Thread {
	private Res res;
	public Test(Res res) {
		this.res = res;
	}
	@Override
	public void run() {
		for (int i = 0; i < 3; i++) {
			System.out.println(Thread.currentThread().getName() + "," + res.getNumber());
		}
	}
	public static void main(String[] args) {
		Res res = new Res();
		Test t1 = new Test(res);
		Test t2 = new Test(res);
		t1.start();
		t2.start();
	}
}

ThreadLoca实现原理

ThreadLoca通过map集合

Map.put(“当前线程”,值);

6、线程安全解决办法

1)Lock锁

Lock lock  = new ReentrantLock();
        lock.lock();
        try{
            //可能会出现线程安全的操作
        }finally{
            //一定在finally中释放锁
            //也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
            lock.unlock();
}

2)同步代码块synchronized

private static Object obj = new Object();
synchronized (obj){
					
}

3)同步方法

 在方法上修饰synchronized 称为同步方法

①非静态同步方法使用的是this锁

class ThreadDemo implements Runnable {
    // 同时多个窗口共享100
    private volatile static int count = 100;// 存放方法区中
    public boolean flag = true;
    public static Object obj = new Object();
    public void run() {
        if(flag){
            while (count > 0) {
                synchronized (this){
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    if (count > 0) {
                        System.out.println(Thread.currentThread().getName() + ",出售" + (100 - count + 1) + "张票");
                        count--;
                    }
                }
            }
        }else{
            while (count > 0) {
                sale();
            }
        }
    }

    public synchronized void sale() {
        try {
            Thread.sleep(10);
        } catch (Exception e) {
            // TODO: handle exception
        }
        if (count > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售" + (100 - count + 1) + "张票");
            count--;
        }
    }
}

public class Test{
    public static void main(String[] args) throws InterruptedException {
        ThreadDemo threadDemo02 = new ThreadDemo();
        Thread t1 = new Thread(threadDemo02, "窗口1");
        Thread t2 = new Thread(threadDemo02, "窗口2");
        t1.start();
        Thread.sleep(40);
        threadDemo02.flag=false;
        t2.start();
    }
}

②静态同步方法使用的是class锁

class ThreadDemo implements Runnable {
    // 同时多个窗口共享100
    private volatile static int count = 100;// 存放方法区中
    public boolean flag = true;
    public static Object obj = new Object();
    public void run() {
        if(flag){
            while (count > 0) {
                synchronized (ThreadDemo.class){
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    if (count > 0) {
                        System.out.println(Thread.currentThread().getName() + ",出售" + (100 - count + 1) + "张票");
                        count--;
                    }
                }
            }
        }else{
            while (count > 0) {
                sale();
            }
        }
    }

    public static synchronized void sale() {
        try {
            Thread.sleep(10);
        } catch (Exception e) {
            // TODO: handle exception
        }
        if (count > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售" + (100 - count + 1) + "张票");
            count--;
        }
    }
}

public class Test{
    public static void main(String[] args) throws InterruptedException {
        ThreadDemo threadDemo02 = new ThreadDemo();
        Thread t1 = new Thread(threadDemo02, "窗口1");
        Thread t2 = new Thread(threadDemo02, "窗口2");
        t1.start();
        Thread.sleep(40);
        threadDemo02.flag=false;
        t2.start();
    }
}

Lock与synchronized 关键字的区别

Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
Lock 接口能被中断地获取锁 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。

Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值