什么是线程安全?如何保证线程安全?

保证线程安全的10个小技巧分享 对于从事后端开发的同学来说,线程安全问题是我们每天都需要考虑的问题。线程安全问题通俗的讲:主要是在多线程的环境下,不同线程同时读和写公共资源(临界资源),导致的数据异常问题。比如:变量a=0,线程1给该变量+1,线程2也给该变量+1。此时,线程3获取a的值有可能不是2,而是1。线程3这不就获取了错误的数据?线程安全问题会直接导致数据异常,从而影响业务功能的正常使用,所以这个问题还是非常严重的。那么,如何解决线程安全问题呢?今天跟大家一起聊聊,保证线程安全的10个小技巧,希望对你有所帮助。 阅读详情
AI摘要

本文介绍了线程安全的概念,探讨了线程不安全的原因,如抢占式执行、多个线程修改同一变量导致的原子性问题、内存可见性和指令重排序。文章详细讲解了如何使用`synchronized`关键字和`volatile`关键字来解决这些问题,以及它们在Java中的应用实例。

目录

一、引入线程安全 👇

二、 线程安全👇

1、线程安全概念 🔍

2、线程不安全的原因 🔍

抢占式执行(罪魁祸首,万恶之源)导致了线程之间的调度是“随机的”

多个线程修改同一个变量 

 修改操作,不是原子的(不可分割的最小单位) 

内存可见性,引起的线程不安全 

指令重排序,引起的线程不安全

三、解决之前的线程不安全问题👇

1、synchronized 关键字-监视器锁monitor lock  🔍

1)synchronized 的特性 

(1) 互斥

(2)刷新内存

(3) 可重入

2)synchronized 使用示例 

1) 直接修饰普通方法:

 2) 修饰静态方法:

3) 修饰代码块: 明确指定锁哪个对象.

2、volatile 关键字 (保证内存可见性) 🔍

 1)引入volatile

2)volatile不保证原子性

💡 总结:


一、引入线程安全 👇

执行以下代码:

package threading;
class Counter {
    public int count = 0;
    void increase() {
        count++;
    }
}

public class ThreadDemo23 {
    public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.count);
    }
}

可以观察代码,我们对两个线程分别累加50000次,结果应该为100000,但是大家看运行结果并非如此,而且可以发现,每次运行的结果都不一样,这是什么原因呢? 

答案就是涉及到了线程安全问题

 

 

本质原因:线程在系统中的调度是无序的/随机的(抢占式执行的) 


二、 线程安全👇

1、线程安全概念 🔍

    如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线 程安全的。 

2、线程不安全的原因 🔍

  • 抢占式执行(罪魁祸首,万恶之源)导致了线程之间的调度是“随机的”

  • 多个线程修改同一个变量 

一个线程修改同一个变量=>安全

多个线程读取同一个变量=>安全

多个线程修改不同变量=> 安全 

  •  修改操作,不是原子的(不可分割的最小单位) 

什么是原子性🐶

我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。

那我们应该如何解决这个问题呢?🐶

是不是只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进 不来了。这样就保证了这段代码的原子性了。 有时也把这个现象叫做同步互斥,表示操作是互相排斥的。

一条 java 语句不一定是原子的,也不一定只是一条指令 比如刚才我们看到的 n++,其实是由三步操作组成的: 1. 从内存把数据读到 CPU 2. 进行数据更新 3. 把数据写回到 CPU

不保证原子性会给多线程带来什么问题🐶

如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。 这点也和线程的抢占式调度密切相关. 如果线程不是 "抢占" 的, 就算没有原子性, 也问题不大.

  • 内存可见性,引起的线程不安全 

          可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到.

  • 指令重排序,引起的线程不安全


三、解决之前的线程不安全问题👇

1、synchronized 关键字-监视器锁monitor lock  🔍

1)对文章初始代码进行修改:既可以保证 ++ 操作就是原子的,不受影响啦

可以将{ }视为厕所,进表示加锁,出表示解锁

void increase() {
        //锁有俩个核心操作,加锁和解锁
        //进入该代码块就会触发加锁,出了代码块,就会触发解锁
        synchronized (this) {
            count++;
        }
    }

 注意:此处的this指的就是counter对象

因此,在上述代码中,两个线程实在竞争同一个锁对象,就会产生锁竞争。

再执行上述代码:就是100000

疑惑为啥以上操作为什么可以解决线程安全问题呢? 🐶

1)synchronized 的特性 

(1) 互斥

      synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到 同一个对象 synchronized 就会阻塞等待.

进入 synchronized 修饰的代码块, 相当于 加锁

退出 synchronized 修饰的代码块, 相当于 解锁 

理解 "阻塞等待". 针对每一把锁, 操作系统内部都维护了一个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝 试进行加锁, 就加不上了, 就会阻塞等待, 一直等到之前的线程解锁之后, 由操作系统唤醒一个新的 线程, 再来获取到这个锁.

注意: 上一个线程解锁之后, 下一个线程并不是立即就能获取到锁. 而是要靠操作系统来 "唤醒". 这 也就是操作系统线程调度的一部分工作.

假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能 获取到锁, 而是和 C 重新竞争, 并不遵守先来后到的规则. 

(2)刷新内存

synchronized 的工作过程: 

  1. 获得互斥锁
  2. 从主内存拷贝变量的最新副本到工作的内存
  3. 执行代码
  4. 将更改后的共享变量的值刷新到主内存
  5. 释放互斥锁 所以 synchronized 也能保证内存可见性. 
(3) 可重入

synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题; 

理解 "把自己锁死" :一个线程没有释放锁, 然后又尝试再次加锁. 

按照之前对于锁的设定, 第二次加锁的时候, 就会阻塞等待. 直到第一次的锁被释放, 才能获取到第 二个锁. 但是释放第一个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无 法进行解锁操作. 这时候就会死锁

当然,Java 中的 synchronized 是 可重入锁, 因此没有上面的问题

 示例:

static class Counter {
    public int count = 0;
    synchronized void increase() {
        count++;
   }
    synchronized void increase2() {
        increase();
   }
}

increase 和 increase2 两个方法都加了 synchronized,

此处的 synchronized 都是针对 this 当前对象加锁的.

在调用 increase2 的时候, 先加了一次锁, 执行到 increase 的时候, 又加了一次锁. (上个锁还没释 放, 相当于连续加两次锁)

这个代码是完全没问题的. 因为 synchronized 是可重入锁.

注意:在可重入锁的内部, 包含了 "线程持有者" 和 "计数器" 两个信息.

如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取 到锁, 并让计数器自增.

解锁的时候计数器递减为 0 的时候, 才真正释放锁. (才能被别的线程获取到) 

2)synchronized 使用示例 

 synchronized 本质上要修改指定对象的 "对象头". 从使用角度来看, synchronized 也势必要搭配一个具 体的对象来使用.

1) 直接修饰普通方法:

锁的 SynchronizedDemo 对象🐶

public class SynchronizedDemo {
    public synchronized void methond() {
   }
}
 2) 修饰静态方法:

锁的 SynchronizedDemo 类的对象🐶

public class SynchronizedDemo {
    public synchronized static void method() {
   }
}
3) 修饰代码块: 明确指定锁哪个对象.

锁当前对象🐶

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            
       }
   }
}

锁类对象🐶

类对象是啥:Counter.class

.java源代码文件,javac =>.class(二进制字节码文件),JVM就可以执行.class文件了,类对象就可以表示这个.class文件的内容~~(描述了类的方方面面的详细信息,比如诶的名字,类的属性,类的方法,)

public class SynchronizedDemo {
    public void method() {
        synchronized (SynchronizedDemo.class) {
       }
   }
}

2、volatile 关键字 (保证内存可见性)🔍

 1)引入volatile

所谓内存可见性,就是多线程环境下,编译器对于代码优化,产生了误判,从而引起了bug,进一步导致了代码的bug

因此我们可以加上 volatile(让编译器对这个场景暂停优化) , 强制读写内存. 速度是慢了, 但是数据变的更准确了.每次都是从内存中重新读取数据

观察以下代码:

package threading;

import java.util.Scanner;

public class ThreadDemo24 {
    public static int flag = 0;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (flag == 0) {

            }
            System.out.println("循环结束,t1结束");
        });
        Thread t2 = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个整数");
            //t2通过控制台输入一个整数,yidanyonghushurule非0的值,此时t1的循环就会立即结束,从而t1线程就会退出
            flag = scanner.nextInt();
        });
        t1.start();
        t2.start();
    }
}

 我们预期的效果应该是输入一个非零的数,线程t1就会停止,但实际上仍然在执行,处在RUNNABLE状态

 为什么会出现以上问题呢?内存可见性的锅!!!

让我们分析一下代码:

直接访问工作内存(实际是 CPU 的寄存器或者 CPU 的缓存), 速度 非常快, 但是可能出现数据不一致的情况.

 

volatile public static int flag = 0;

加上volatile关键字,此时编译器就可以保证每次都是重新从内存读取flag变量的值,

此时t2修改flag,t1就可以立即感知到了,t1就可以正确退了

2)volatile不保证原子性

这个是最初的演示线程安全的代码.

给 increase 方法去掉 synchronized

给 count 加上 volatile 关键字.

static class Counter {
    volatile public int count = 0;
    void increase() {
        count++;
   }
}
public static void main(String[] args) throws InterruptedException {
    final Counter counter = new Counter();
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(counter.count);
}

此时可以看到, 最终 count 的值仍然无法保证是 100000 

💡 总结:
  • volatile不保证原子性
  • volatile也能禁止指令重排序
  • volatile 适用一个线程读,一个线程写的情况
  • synchronized则是多个线程写 
  • volatile 和 synchronized 有着本质的区别. synchronized 能够保证原子性(也能保证内存可见性), volatile 保证的是内存可见 性
static class Counter {
    public int flag = 0;
}
public static void main(String[] args) {
    Counter counter = new Counter();
    Thread t1 = new Thread(() -> {
        while (true) {
            synchronized (counter) {
  if (counter.flag != 0) {
                    break;
               }
           }
            // do nothing
       }
        System.out.println("循环结束!");
   });
    Thread t2 = new Thread(() -> {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入一个整数:");
        counter.flag = scanner.nextInt();
   });
    t1.start();
    t2.start();
}

上面代码:

去掉 flag 的 volatile

给 t1 的循环内部加上 synchronized, 并借助 counter 对象加锁. 

运行结果是可以正常结束的

因此 synchronized是可以保证内存可见性的

 

 补充: 指令重排序,也是编译器优化的策略,调整了代码的执行顺序,让程序更高效,前提也是保证整体逻辑不变

 💡 总结:(面试题)

线程不安全的原因:

【根本原因】==操作系统上的线程是“抢占式执行”的,线程调度是随机的,==这是线程不安全的一个主要原因。随机调度会导致在多线程环境下,程序的执行顺序不确定,程序员必须确保无论哪种执行顺序,代码都能正常运行。
【代码结构】共享资源:多个线程同时访问并修改共享的数据或资源。当多个线程同时访问和修改共享资源时容易引发竞态条件和数据不一致的问题。
①一个线程修改一个变量是安全的
②多个线程修改一个变量是不安全的
③多个线程修改不同变量是安全的
【直接原因】多线程操作不是“原子的”。多线程操作中的原子性指的是一个操作是不可中断的,要么全部执行完成,要么都不执行,不能被其他线程干扰。这对于并发编程非常重要,因为如果一个操作在执行过程中被中断,可能导致数据不一致或者其他意外情况发生。(在上述多线程操作中,count++操作不是“原子的”,而是由多个CPU指令组成的,一个线程执行这些指令时,可能会在执行过程中被抢占,从而给其他线程“可乘之机”。要保证原子性操作,每个CPU指令都应该是“原子的”,即要么完全执行,要么完全不执行。)
内存可见性问题:在多线程环境下调用不可重入的函数(即不支持多线程调用的函数),可能导致数据混乱或程序崩溃。
指令重排序问题:在多线程环境下,由于编译器或处理器对指令进行重排序优化,可能导致预期之外的程序行为。

 

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。


                        

【Java 多线程 1】java多线程并发编程 一、Synchronized保证原子性 public class Test { private static int number = 0; private static Object obj = new Object(); public static void main(String[] args) throws InterruptedException { Runnable increment = () -> { for (int 阅读详情

相关推荐

高效IT运维报告编写指南:实战经验与模板分享

本文提供了一份全面的IT运维报告编写指南,涵盖实战经验与实用模板分享。文章详细解析了高效运维报告的核心要素,包括结构设计、数据收集自动化和可视化技巧,并提供了月度报告和故障分析报告模板。通过自动化工具和写作技巧,帮助团队提升报告质量,支持决策和问题追踪。

u5v6w7的博客 926

详解什么是线程安全?

并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。那么,操作系统视如何实现这种并发的呢?现在我们用到操作系统,无论是Windows、Linux还是MacOS等其实都是多用户多任务分时操作系统。使用这些操作系统的用户是可以“同时”干多件事的。但是实际上,对于单CPU的计算机来说,在CPU中,同一时间是只能干一件事儿的。

白话机器学习 5080

别再纠结高侧低侧了!用INA199/INA219这类集成电流检测芯片,5分钟搞定精准电流测量

本文详细解析了使用INA199/INA219等集成电流检测芯片实现精准电流测量的高效方案。通过对比传统分立元件方案,突出集成芯片在精度、温度稳定性和设计简化方面的优势,并提供硬件设计避坑指南和软件实现最佳实践,帮助开发者快速掌握电流检测技术。

weixin_42685438的博客 470

线程同步之可重入锁

可重入锁与不可重入锁的比较 可重入锁是指当前已经获得锁的线程可以再次获取该锁且不会陷入阻塞。这句话似乎不是很好理解,我们可以从理解不可重入锁的概念来理解。不可重入锁是指在一个线程调用某方法获取锁对象之后,其他线程再次请求获取该锁,都会陷入等待,直到当前线程释放锁之后,其他对象才有机会获得锁,并且在这期间,如果已经获得锁的线程再次执行获得锁的方法,也会使得自己被阻塞,同时释放持有的锁资源。可重入锁和...

马一凡的博客 1172

Blender地形建模实战:5步搞定Gazebo仿真地形(附避坑指南)

本文提供了一份详细的Blender地形建模实战指南,旨在帮助开发者快速创建Gazebo仿真地形。文章通过五个核心步骤,从环境准备、地形雕刻、模型优化导出,到创建Gazebo模型包及问题排错,系统讲解了如何将Blender中的地形模型成功导入Gazebo进行机器人仿真,并重点分享了关键的避坑经验。

weixin_28475533的博客 344

优化求解器性能:当分支切割算法中的gap停滞不前时如何应对

本文深入探讨了当分支切割算法求解复杂整数规划问题时,gap停滞不前的根本原因与应对策略。文章指出gap停滞源于上界(找不到更优整数解)或下界(证明过程缓慢)提升受阻,并提供了从模型改写、自定义割平面、调整搜索策略到参数调优等一系列实战方法,帮助优化工程师有效打破求解僵局,推动gap下降。

weixin_29294597的博客 381

线程安全(干货满满!)

线程安全(Thread safety)是多线程编程中的一个重要概念,它指的是在多线程环境下,一个程序或者代码段在并发访问时,能够正确地保持其预期的行为和状态,而不会出现意外的错误或者不一致的结果。具体来说,当多个线程同时访问共享资源(如共享变量、共享数据结构、共享文件等)时,线程安全的编程技术和方法旨在解决并发访问问题,确保程序的正确性和稳定性。

2302_78814792的博客 3380

使用Python从图像中提取表格

python 实现图片表格提取

qq_30895747的博客 2570

Java 如何保证线程安全

在多线程环境下,多个线程可能会同时访问和修改共享资源(如变量、数据结构等),如果不能正确管理这些操作,可能导致以下问题:为了确保程序在多线程环境下的正确性和一致性,需要采取措施保证线程安全。以下是 Java 中常用的一些方法来保证线程安全: 是 Java 提供的内置关键字,用于实现对象级别的锁。它可以修饰方法或代码块。同步方法: 这里的 锁定的是当前实例(),只有持有该锁的线程才能执行方法。 同步代码块: 注意事项:Java 提供了 类,可以显式地管理线程间的锁。它比 更灵活。 特点:如果

zru_9602的博客 1637

【图像分类】卷积神经网络之VGGNet网络模型实现花卉图像识别(附代码和数据集,PyTorch框架)

卷积神经网络之VGGNet网络模型实现花卉图像识别(附代码和数据集,PyTorch框架)

学无止境、积少成多、厚积薄发 2430

uniapp(全端兼容) - 最新详细实现瀑布流列表及布局设计,uni-app全平台兼容的瀑布流示例,view容器自适应瀑布流布局,图片瀑布流布局、图像+文字卡片式瀑布流,类似小红书(详细示例代码)

uni-app,瀑布流,安卓app,苹果app,微信小程序,移动端,H5网页,手机网站,微信小程序、支付宝小程序,手机app端,nvue,瀑布流布局,如何实现图片瀑布流,怎么做文字+图像瀑布流,uniapp仿小红书笔记瀑布流效果,uniAPP仿快手抖音布局列表,瀑布流列表详细教程,编译所有平台都能用的瀑布流列表,商品瀑布流列表,图文列表,小程序瀑布流,页面触底懒加载,瀑布流插件,详细示例代码教程,uni-app H5实现瀑布流效果,uniapp小程序做瀑布流,纯css瀑布流,用uniapp写瀑布流商品列表,

✅ 精选推荐文章 1万+

保证线程安全的三种方式

1,什么是线程安全 2,如何保证线程安全 3,线程安全的三种实现方式具体实现 4,总结

weixin_49986365的博客 5574

从后端角度浅谈抗辐射设计----三模冗余(TMR)

三模冗余设计(TMR),是最常用的一种容错技术,三个模块采取相同的操作,以多数相同的输出作为表决系统的正确输出,即3取2,因为三个模块是相互独立的,而其中两个模块同时出错的概率是极小的,因此便可保证系统输出的可信度。然而这个设计也会存在面积浪费,功耗的增加,而且由于是冗余的设计,在后续进行综合或者pr的时候会被工具优化,因此需要对模块进行don’t touch设置;通过增值表得到,逻辑关系表达式为:Q=(Q1&Q2)|(Q1&Q3)|(Q2&Q3),通过工艺库的stdcell进行搭建,得到如下电路。

芯冰乐 327

说说怎么保证线程安全

得分点 原子类 volatile 锁 标准回答 Java 保证线程安全的方式有很多,其中较为常用的有3种,按照资源占用情况由轻到重排列,这三种保证线程安全的方式分别是原子类、volatile、锁。JDK从1.5开始提供了java.util.concurrent.atomic包,这个包中的原子操作提供了一种用法简单、性能高效、线程安全的更新一个变量的方式。在atomic包里一共提供了17个类,按功能可以归纳为4中类型的原子更新方式,分别是原子更新基本类型、原子更新引用类型、原子更新属性、原子更新数组。无论原

naorenteng的博客 2079

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Volatile能不能保证线程安全?

例如,在对象的初始化过程中,如果涉及到 Volatile 变量,能确保对象的初始化步骤按照正确的顺序执行,避免其他线程获取到未完全初始化的对象。例如,对于简单的状态标志共享场景,可以先使用 Volatile 保证标志的可见性,然后在具体的操作过程中(如根据标志执行一系列复杂操作)再使用 Synchronized 或原子类来保证操作的原子性和线程安全。原子性要求一个操作或一组操作作为一个整体执行,而对于一些常见的需要原子性的操作,如共享变量的自增、自减等操作,仅仅使用 Volatile 是不够的。

qq_62097431的博客 684

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Redis 是如何保证线程安全的?

单线程模型:Redis 使用单线程处理所有请求,避免了多线程带来的上下文切换和锁竞争问题,天然保证了线程安全。原子操作:Redis 提供了许多原子操作(如 SET、GET、INCR、LPUSH 等),保证了操作的原子性,不会在执行过程中被其他操作打断。事务支持:通过 MULTI/EXEC 命令,Redis 能够将多个操作封装在一起,并确保这些操作是原子的。乐观锁与 Lua 脚本:通过 WATCH 和 Lua 脚本,Redis 提供了额外的原子操作保障,能够在复杂场景下保持线程安全。

zru_9602的博客 1238

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

什么是线程安全,如何保证线程安全?

线程安全(Thread Safety)是指多线程环境下,多个线程同时访问和操作同一资源时,程序的行为仍然是正确的,即不会因为多线程的并发操作而导致数据不一致或程序崩溃。

qq_43287763的博客 1235

ConcurrentHashMap是如何保证线程安全的?

ConcurrentHashMap相当于是HashMap的多线程版本,它的功能本质上和HashMap没什么区别。因为HashMap在并发操作的时候会出现各种问题,比如死循环问题、数据覆盖等问题。而这些问题,只要使用ConcurrentHashMap就可以完美地解决。那问题来到了,ConcurrentHashMap它是如何保证线程安全的呢? 1、JDK1.7实现原理 首先,我们来看JDK1.7中ConcurrentHashMap的底层结构,它基本延续了HashMap的设计,采用的是数组加链...

Tom弹架构 1万+

什么情况下volatile能够保证线程安全

刚才虽然说,volatile关键字不一定能够保证线程安全的问题,其实,在大多数情况下volatile还是可以保证变量的线程安全问题的。所以,在满足以下两个条件的情况下,volatile就能保证变量的线程安全问题: 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。 变量不需要与其他状态变量共同参与不变约束。 ...

qq_39326137的博客 2247

什么是线程安全、怎么保证线程安全

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。

qq_52240237的博客 1076

什么是线程安全? 如何保证线程安全?

随着硬件技术的快速发展(比如多核处理器,超线程技术),我们通常会在代码中使用多线程(比如线程池)来提高性能,但是,多线程又会带来线程安全问题。因此,本文将深入探讨Java中的线程安全问题。

weixin_42029738的博客 1181

什么是线程安全?如何保证线程安全?Java中保证线程安全的方法有哪些?【重要】...

一、什么是线程安全? 简单来说,线程安全是多个线程访问同一段代码,不会造成不确定的结果。 ...

m0_51697147的博客 313

多线程—保证线程安全

比如,线程A(等待某些计算资源)、线程B(等待某些计算资源)和线程C竞争锁对象,线程A获得锁后发现自己需要的数据还没有,就执行结束释放锁,线程B竞争到锁也发现自己需要的数据还没有,也释放锁,线程A又抢到锁,这样循环往复,线程C长时间没有得到锁,出现线程饿死现象。当线程进入到synchronized修饰的代码块,如果其他线程还未获得锁,该线程就进行加锁,此时其他线程再想进入同一个锁对象修饰的代码块中,就会阻塞等待,直到加锁的代码块执行结束,线程解锁,此时其他线程再来竞争锁。计数器的值减为0时,锁才被释放。

sniper_fandc的博客 1266

ConcurrentHashMap如何保证线程安全?

ConcurrentHashMap 在 JDK 1.7 时使用的是数据加链表的形式实现的,其中数组分为两类:大数组 Segment 和小数组 HashEntry,而加锁是通过给 Segment 添加 ReentrantLock 锁来实现线程安全的。而 JDK 1.8 中 ConcurrentHashMap 使用的是数组+链表/红黑树的方式实现的,它是通过 CAS 或 synchronized 来实现线程安全的,并且它的锁粒度更小,查询性能也更高。

m0_69632475的博客 2804

ConcurrentHashMap 如何保证线程安全的?

常见面试问题Concurrent

m0_74037817的博客 1784

保证线程安全的处理策列

weakCompareAndSetInt中会再次通过偏移量offset从对象o中获取字段的当前值,如果这期间字段值被其他线程修改,那么此时获取到的当前值就不等于期望值,就不会执行更新操作,只有确保在修改字段值之前,字段的当前值与预期值依然相等,才能执行更新操作,从而确保了原子操作的安全性。1、单独操作:上述我们的demo中,出现了两个线程A、B,它们分别调用get()、put()方法,虽然看起来像是在组合操作,但实际上对于单个线程而言,这是单独操作,是线程安全的。线程3这不就获取了错误的数据?

0 948
上一篇: 贪心算法每日一题(摆动序列)
下一篇: 总结常见的六大锁策略
一枚努力的程序猿哇
博客等级 码龄5年 274粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值