当前位置: 首页 > news >正文

国内个人网站吴江住宅城乡建设局网站

国内个人网站,吴江住宅城乡建设局网站,做同城网站还有机会吗,厦门网站建设 孚珀科技目录 线程安全线程安全问题的原因 synchronized 关键字-监视器锁monitor locksynchronized的特性互斥刷新内存可重入 synchronized使用范例 volatilevolatile能保证内存可见性volatile不保证原子性synchronized 也能保证内存可见性 wait 和 notifywait()方法notify()方法notify… 目录 线程安全线程安全问题的原因 synchronized 关键字-监视器锁monitor locksynchronized的特性互斥刷新内存可重入 synchronized使用范例 volatilevolatile能保证内存可见性volatile不保证原子性synchronized 也能保证内存可见性 wait 和 notifywait()方法notify()方法notifyAll()方法 线程安全 如果多线程环境下代码运行的结果是符合我们预期的即在单线程环境应该的结果则说这个程序是线程安全的。 线程安全问题的原因 [根本原因]多个线程之间的调度顺序是“随机”的操作系统使用“抢占式”执行的策略来调度线程 多个线程同时修改同一个遍历容易产生线程安全问题 3个条件 多个修改同一个 进行的修改不是“原子的”如果修改操作能按照原子的形式完成就不会有线程安全问题原子即不可再分 内存可见性引起的线程安全问题 指令重排序引起的线程安全问题 以上五个原因只有第3个原因能想办法修正通过“加锁”的方式把一组操作给打包成一个“原子”的操作。此处的原子就是通过锁进行“互斥”我这个线程工作的时候其他线程无法工作 原子性 什么是原子性 我们把一段代码想象成一个房间每个线程就是要进入这个房间的人。如果没有任何机制保证A进入房间之后还没有出来B 是不是也可以进入房间打断 A 在房间里的隐私。这个就是不具备原子性的。 那我们应该如何解决这个问题呢是不是只要给房间加一把锁A 进去就把门锁上其他人是不是就进不来了。这样就保证了这段代码的原子性了。 有时也把这个现象叫做同步互斥表示操作是互相排斥的。 如果一个线程正在对一个变量操作中途其他线程插入进来了如果这个操作被打断了结果就可能是错误的。 可见性 一个线程对共享变量值的修改能够及时地被其他线程看到 代码顺序性 什么是代码重排序 一段代码是这样的 去前台取下 U 盘去教室写 10 分钟作业去前台取下快递 ​ 如果是在单线程情况下JVM、CPU指令集会对其进行优化比如按 1-3-2的方式执行也是没问题可以少跑一次前台。这种叫做指令重排序 编译器对于指令重排序的前提是 “保持逻辑不发生变化”. 这一点在单线程环境下比较容易判断, 但是在多线程环境下就没那么容易了, 多线程的代码执行复杂程度更高, 编译器很难在编译阶段对代码的执行效果进行预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价. synchronized 关键字-监视器锁monitor lock 代码中的锁就是让多个线程同一时刻只有一个线程能使用这个变量 synchronized的特性 互斥 synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同一个对象 synchronized 就会阻塞等待. 进入 synchronized 修饰的代码块, 相当于 加锁退出 synchronized 修饰的代码块, 相当于 解锁 //进入就针对当前对象“加锁” synchronized public void increase(){count; } //出来就针对当前对象“解锁”synchronized关键字最主要有以下3种应用方式 修饰实例方法作用于当前实例加锁进入同步代码前要获得当前实例的锁 修饰静态方法作用于当前类对象加锁进入同步代码前要获得当前类对象的锁 修饰代码块指定加锁对象对给定对象加锁进入同步代码块前要获得给定对象的锁。这里可以给实例对象的名称Test testnew Test()的test也可以给this对象代表当前实例也可以给当前类的class对象作为锁 思考通过加锁操作之后把并发执行串行执行了此时多线程还有存在的意义吗 答因为两个线程可能有一部分代码是串行执行的有一部分是并发执行的这仍然比纯粹的串行执行效率要高 理解 “阻塞等待”. 针对每一把锁, 操作系统内部都维护了一个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝试进行加锁, 就加不上了, 就会阻塞等待, 一直等到之前的线程解锁之后, 由操作系统唤醒一个新的线程, 再来获取到这个锁. 注意: 上一个线程解锁之后, 下一个线程并不是立即就能获取到锁. 而是要靠操作系统来 “唤醒”. 这也就是操作系统线程调度的一部分工作. 假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能获取到锁, 而是和 C 重新竞争, 并不遵守先来后到的规则 synchronized用的锁是存在Java对象里的。 synchronized进行加锁解锁其实是以“对象”为维度进行展开的。 加锁目的是为了互斥使用资源。互斥的修改变量 使用synchronized的时候其实是指定了某个具体的对象进行加锁当synchronized直接修饰方法时此时就相当于是针对this加锁修饰方法相当于这段代码的简化写法[不存在所谓的“同步方法”的概念] class Counter{public int count0;public void increace(){synchronized (this){//this就是下面调用的countercount;}}public void increace2(){count;}public synchronized static void func(){synchronized (Counter.class){}} } public class Demo {public static void main(String[] args) throws InterruptedException {Counter counternew Counter();Thread t1new Thread(()-{for (int i 0; i 50000; i) {counter.increace();//这里t1的counter和下面t2的counter进行锁竞争/锁冲突}});Thread t2new Thread(()-{for (int i 0; i 50000; i) {counter.increace();//这里t2的counter和上面t1的counter进行锁竞争/锁冲突}});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.count);} }如果是两个线程针对同一个对象进行加锁就会出现锁竞争/锁冲突一个线程能加锁成功另一个线程阻塞等待 如果是两个线程针对不同对象进行加锁就不会出现锁竞争/锁冲突也就不存在阻塞等待的操作了 因此具体针对哪个对象加锁不重要重要的是两个线程是不是针对同一个对象加锁 思考如果接下来的代码里一个线程加锁了一个线程没加锁此时是否还会存在线程安全问题答单方面加锁等于没加锁必须得多个线程都对同一个对象加锁才有意义 synchronized的底层是使用操作系统的mutex lock实现的. synchronized有且只有一条规则 当两个线程针对同一个对象加锁的时候就会出现锁竞争/锁冲突。一个线程能先拿到锁另一个线程就会阻塞等待BLOCKED。直到第一个线程释放了锁之后第二个线程才可能获取到锁才能继续往下执行。 刷新内存 synchronized 的工作过程: 获得互斥锁 从主内存拷贝变量的最新副本到工作的内存 执行代码 将更改后的共享变量的值刷新到主内存 释放互斥锁 可重入 synchronized 同步块对同一条线程来说是可重入的不会出现自己把自己锁死的问题 理解 把自己锁死 一个线程没有释放锁, 然后又尝试再次加锁. // 第一次加锁, 加锁成功 lock(); // 第二次加锁, 锁已经被占用, 阻塞等待. lock();按照之前对于锁的设定, 第二次加锁的时候, 就会阻塞等待. 直到第一次的锁被释放, 才能获取到第二个锁. 但是释放第一个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无法进行解锁操作. 这时候就会 死锁. 这样的锁称为 不可重入锁. Java 中的 synchronized 是 可重入锁, 因此没有上面的问题 代码示例 在下面的代码中, increase 和 increase2 两个方法都加了 synchronized, 此处的 synchronized 都是针对 this 当前对象加锁的.在调用 increase2 的时候, 先加了一次锁, 执行到 increase 的时候, 又加了一次锁. (上个锁还没释放, 相当于连续加两次锁) 这个代码是完全没问题的. 因为 synchronized 是可重入锁. static class Counter {public int count 0;synchronized void increase() {count;}synchronized void increase2() {increase();} }在可重入锁的内部, 包含了 “线程持有者” 和 “计数器” 两个信息. 如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取到锁, 并让计数器自增. 解锁的时候计数器递减为 0 的时候, 才真正释放锁. (才能被别的线程获取到) synchronized使用范例 synchronized 本质上要修改指定对象的 “对象头”. 从使用角度来看, synchronized 也势必要搭配一个具体的对象来使用. 直接修饰普通方法:锁的 SynchronizedDemo 对象 public class SynchronizedDemo {public synchronized void methond() {} }修饰静态方法: 锁的 SynchronizedDemo 类的对象 public class SynchronizedDemo {public synchronized static void method() {} }修饰代码块: 明确指定锁哪个对象. 锁当前对象 public class SynchronizedDemo {public void method() {synchronized (this) {//Test testnew Test()的test也行}} }锁类对象 public class SynchronizedDemo {public void method() {synchronized (SynchronizedDemo.class) {}} }volatile volatile能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性”. 代码在写入 volatile 修饰的变量的时候, 改变线程工作内存中volatile变量副本的值将改变后的副本的值从工作内存刷新到主内存 代码在读取 volatile 修饰的变量的时候, 从主内存中读取volatile变量的最新值到线程的工作内存中 从工作内存中读取volatile变量的副本 代码示例 在这个代码中 创建两个线程 t1 和 t2t1 中包含一个循环, 这个循环以 flag 0 为循环条件.t2 中从键盘读入一个整数, 并把这个整数赋值给 flag.预期当用户输入非 0 的值的时候, t1 线程结束. static class Counter {public int flag 0; } public static void main(String[] args) {Counter counter new Counter();Thread t1 new Thread(() - {while (counter.flag 0) {// 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(); } // 执行效果 // 当用户输入非0值时, t1 线程循环不会结束. (这显然是一个 bug) // 注意这里直接在控制台是看不出的t1 读的是自己工作内存中的内容. 当 t2 对 flag 变量进行修改, 此时 t1 感知不到 flag 的变化. 如果给 flag 加上 volatile static class Counter {public volatile int flag 0; } // 执行效果 // 当用户输入非0值时, t1 线程循环能够立即结束. // 注意这里直接在控制台同样是看不出的volatile不保证原子性 volatile 和 synchronized 有着本质的区别. synchronized 能够保证原子性, volatile 保证的是内存可见性. synchronized 也能保证内存可见性 synchronized 既能保证原子性, 也能保证内存可见性. 内存可见性问题 编译器优化内存模型多线程 volatile保证的是内存可见性不是原子性 内存可见性和加锁描述了线程安全问题的典型情况和处理方式 wait 和 notify 由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序. 完成这个协调工作, 主要涉及到三个方法 wait() / wait(long timeout): 让当前线程进入等待状态.notify() / notifyAll(): 唤醒在当前对象上等待的线程. 注意: wait, notify, notifyAll 都是 Object 类的方法. wait等待和notify通知就是一个用来协调线程顺序的重要工具 这两个方法都是Object提供的方法随便找个对象都可以调用 当wait引起线程阻塞之后可以使用interrupt方法把线程唤醒打断当前线程的阻塞状态 wait()方法 wait在执行的时候会做三件事 解锁。object.wait就会尝试针对object对象解锁阻塞等待当被其他线程唤醒之后就会尝试重新加锁加锁成功wait执行完毕继续往下执行其他逻辑。 wait要解锁前提是先能加上锁 核心解决思路先加锁在synchronized里头再wait这样子的wait就会一直阻塞到其他线程进行notify了 注意事项 要想让notify能够顺利唤醒wait就需要确保wait和notify都是使用同一个对象调用的。wait和notify都需要放到synchronized之内的。虽然notify不涉及“解锁操作”但是Java也强制要求notify要放到synchronized中。系统的原生api中就没有这个要求如果进行notify的时候另一个线程并没有处于wait状态此时notify相当于“空打一炮”不会有任何副作用 代码示例: 观察wait()方法使用 public static void main(String[] args) throws InterruptedException {Object object new Object();synchronized (object) {System.out.println(等待中);object.wait();System.out.println(等待结束);} }这样在执行到object.wait()之后就一直等待下去那么程序肯定不能一直这么等待下去了。这个时候就需要使用到了另外一个方法唤醒的方法notify() 线程可能有多个比如可以有n个线程进行wait一个线程负责notifynotify操作只会唤醒一个线程。具体是唤醒了哪个线程是随机的 wait和sleep的区别 sleep有个明确的时间到达时间自然就会被唤醒也能提前唤醒使用interruptwait默认是个死等一直等到其他线程notifywait也能被interrupt提前唤醒 notify()方法 notify 方法是唤醒等待的线程. 方法notify()也要在同步方法或同步块中调用该方法是用来通知那些可能等待该对象的对象锁的其它线程对其发出通知notify并使它们重新获取该对象的对象锁。如果有多个线程等待则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 “先来后到”)在notify()方法后当前线程不会马上释放该对象锁要等到执行notify()方法的线程将程序执行完也就是退出同步代码块之后才会释放对象锁。 代码示例: 使用notify()方法唤醒线程 创建 WaitTask 类, 对应一个线程, run 内部循环调用 wait.创建 NotifyTask 类, 对应另一个线程, 在 run 内部调用一次 notify注意WaitTask 和 NotifyTask 内部持有同一个 Object locker. WaitTask 和 NotifyTask 要想配合就需要搭配同一个 Object. static class WaitTask implements Runnable {private Object locker;public WaitTask(Object locker) {this.locker locker;}Overridepublic void run() {synchronized (locker) {while (true) {try {System.out.println(wait 开始);locker.wait();System.out.println(wait 结束);} catch (InterruptedException e) {e.printStackTrace();}}}} } static class NotifyTask implements Runnable {private Object locker;public NotifyTask(Object locker) {this.locker locker;}Overridepublic void run() {synchronized (locker) {System.out.println(notify 开始);locker.notify();System.out.println(notify 结束);}} } public static void main(String[] args) throws InterruptedException {Object locker new Object();Thread t1 new Thread(new WaitTask(locker));Thread t2 new Thread(new NotifyTask(locker));t1.start();Thread.sleep(1000);t2.start(); }notifyAll()方法 notify方法只是唤醒某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程. 如果就想唤醒某个指定的线程就可以让不同的线程使用不同的对象来进行 wait想唤醒谁就可以使用对应的对象来notify 范例修改 NotifyTask 中的 run 方法, 把 notify 替换成 notifyAll public void run() {synchronized (locker) {System.out.println(notify 开始);locker.notifyAll();System.out.println(notify 结束);} }**注意**虽然是同时唤醒 3 个线程, 但是这 3 个线程需要竞争锁. 所以并不是同时执行, 而仍然是有先有后的执行. wait和sleep的区别面试题 sleep有个明确的时间到达时间自然就会被唤醒也能提前唤醒使用interruptwait默认是个死等一直等到其他线程notifywait也能被interrupt提前唤醒wait 需要搭配 synchronized 使用sleep 不需要.wait 是 Object 的方法 sleep 是 Thread 的静态方法.
http://www.zqtcl.cn/news/17102/

相关文章:

  • 苏州园区已经烂掉了网站搜索引擎优化主要方法
  • c2c代表网站有哪些怎么开个网站
  • 天津重型网站建设推荐编程的基础知识
  • win7 iis asp网站配置文件怎么做一个企业的网站
  • 如何借助网站打广告品牌推广方案设计
  • 建站服务器缤纷销客crm
  • 博客网站开发网站如何做延迟加载
  • 个人网站建设心得体会wordpress随机文本
  • 网站建设网易工业产品外观设计
  • led照明企业网站模板中国域名门户网站
  • o2o网站建站如何让各大搜索引擎识别新建网站
  • 杭州专业网站制作永州市建设局网站
  • 装饰网站开发背景工商营业执照咨询电话
  • 如何做自己微网站网站模板一般用什么软件做
  • wordpress做一个网站404引导大宗交易平台
  • 南通网站seo报价健身网站开发过程中遇到的麻烦
  • 房产网站如何做柳州建网站
  • 广州建设网站怎么做wordpress最新列表
  • 全flash网站制作教程简约个人主页
  • 广西建设厅网站公布国外大气的网站
  • 网站建设评比考核报告100%能上热门的短视频素材
  • 如何做电影网站狼视听wordpress如何安装模板
  • 网站视觉首页怎么做网站开发图片压缩
  • 网站引导制作wordpress qq邮箱订阅
  • 沈阳网站建设syfzkj江苏seo和网络推广
  • 怎么做网站旅游宣传公司建设一个网站
  • 深圳网站建设公司公司深圳音乐制作公司
  • 个人网站命名网站建设教材下载
  • 决定网站打开的速度吗杭州网站公司哪家服务好
  • wordpress 子postseo职位具体做什么