拓冰建站拓冰建站
首页 / 资讯中心 / 正文

多线程02

一、线程安全问题1.线程安全是啥就是一段代码在多线程中并发执行后和我们的预期结果不一样例如两个线程同时给一个变量但是操作没有加锁这时候就可能导致最后的结果和我们想要的结果不一样就出现了bug2.原因因为操作系统对于线程的调度是随机的是抢占式执行的所以不知道cpu这时候还是下时候执行的是哪个指令可以加锁多个线程同时修改同一个变量就可能导致操作两次但是值只改变1次的结果。可以加锁修改的操作不是原子的指令不是只有一个是多个因为操作系统对于线程的调度是抢占式执行的所以这个修改操作的多个指令中间是否穿插了其他的指令就不确定内存的可见性是因为编译器优化比如我们在锁里面使用的一个变量需要我们在锁外面输入结果来确定是否来解锁因为计算机等待我们输入的时间是不确定的所以这时候编译器就会把这个对象的变量的值直接存到它的工作区直接从工作区来拿取值来判断是否解锁这时候我们输入结果会存储在内存中但是它只会在工作区拿值所以内存中的值的改变不会影响工作区的值这就是内存可见性问题可以使用volatile关键字指令重排序问题二、wait 和 notify 简单了解wait / notify可以协调线程之间执行逻辑的顺序。可以让后执行的逻辑等待先执行的逻辑先跑虽然无法直接干预操作系统对于线程的调度顺序但是可以让后执行的逻辑线程等待等到先执行的逻辑跑完了再执行后面的逻辑。join也是等另一个线程彻底执行完之后才执行。wait 是等到另一个线程执行 notify之后继续走不需要另一个线程执行完。当多个线程竞争同一把锁的时候获取锁的线程释放后接下来是哪个线程拿到锁这个事情是不确定的。因为操作系统对线程的调度是随机的此时一个A线程拿到锁其他线程都是再线程阻塞等待的状态这时候A线程释放锁接下来是哪个线程拿到锁是不确定的有可能还是A线程再次拿到这个锁。三、线程饥饿/线程饿死我们把每个线程都当成一个鸟宝宝cpu就是鸟妈妈鸟妈妈需要每隔一段时间给鸟宝宝喂食物来确保鸟宝宝的正常成长。这样的场景就是wait和notify的经典场景。当拿到锁的线程发现要执行的任务还不成熟就会使用wait阻塞等待。wait和notify都是Object的方法因为java 中所有的对象都是Object的子类所以java中的任意对象都提供了wait和notify方法。四、wait 方法当这个线程拿到锁之后使用wait方法之后就会把锁给释放让接下来的线程来拿到锁执行它的逻辑一直等到其他线程对这个锁使用 notify 方法才有可能随即唤醒该锁下的 wait 线程。其实为什么要释放锁呢假设wait 不会释放锁。我们使用wait方法的前提是有一个锁对象我们对这个锁使用 wait 方法来触发等待我们等待的过程中必须得把这个锁给释放了如果不释放其他线程执行逻辑准备拿这个锁的时候就会触发锁阻塞等待等待该线程释放锁但是该线程因为使用 wait 等待其他线程使用 notify 这时候就会因为这个锁的释放问题触发了死循环。所以 wait 等待的时候会把锁对象给释放掉。wait 的锁对象必须是和 synchronized 的锁对象是同一个下面的代码就是一段wait和notify的简单示例。public class J6_03_blogs { public static void main(String[] args) throws InterruptedException { Object locker1 new Object(); Thread t1 new Thread(() - { synchronized(locker1){ try { System.out.println(t1线程上锁等待); locker1.wait(); System.out.println(t1线程 wait 等待结束); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); Thread t2 new Thread(() - { synchronized (locker1){ locker1.notify(); System.out.println(t2 线程唤醒 t1线程因为 wait 方法阻塞等待); } }); t1.start(); Thread.sleep(3000); t2.start(); } }五、单例设计模式设计模式是属于我们程序员的棋谱它把一些典型的问题场景整理了出来并且针对这些场景写出了代码应该怎么写给出了具体方案和指导建议。单例设计模式就是强制要求一个类不可以创建多个对象就是这个类只能由一个对象具体核心做法就是给构造函数私有化这时候谁都拿不到它的构造方法只能这个类内部的函数可以拿到构造方法这时候在这个类里面的方法中只使用一次构造方法就可以实现只有一个类对象的单例模式。六、饿汉模式饿汉模式是尽量早的创建该类的实例化在构造的时候就构造好了这个类的对象。在这个类里面私有化构造方法私有一个类的对象最后我们要获取这个类对象都是通过getInstance这个方法来获取类对象这就保证了这个类只实例化出了一个对象。饿汉模式在开始的时候就创建好了后续只是读取不涉及创建所以在多线程下是线程安全的。public class Singleton { private static Singleton instance new Singleton(); private Singleton() {} public Singleton getInstance() { return instance; } }七、懒汉模式懒汉模式是尽量晚的创建对象等到使用的时候才会创建。这里获取类对象的时候涉及到了对象的创建创建操作不是原子的所以我们需要对这个创建操作进行分析。public class SingletonLazy { private static SingletonLazy instance null; private SingletonLazy() { } public SingletonLazy getInstance() { if(instance null) { instance new SingletonLazy(); } return instance; } }7.1 存在多个线程同时来获取对象的时候会有多次构造。单独给创建那一行代码加锁这样加锁保证了创建对象的安全但是降低了效率当多个线程来访问的时候因为创建操作不知道多长时间所以这么多线程都得阻塞等待在synchronized(this)这个操作上后续也会创建对象还是无法解决问题。public class SingletonLazy { private static SingletonLazy instance null; private SingletonLazy() { } public SingletonLazy getInstance() { if(instance null) { ////////////////// synchronized(this) { instance new SingletonLazy(); } ////////////////// } return instance; } }7.2 存在多个线程同时来获取对象的时候会有多次构造。给判断函数加上锁这时候第一个拿到锁的线程可以执行下面判断和创建的操作并且其他线程都会阻塞在第一个锁对象获取的这一步。等带第一个线程执行完它的逻辑之后才会解除阻塞等待这种方式虽然解决了多线程的创建多个对象的过程但是会因为第一个线程加锁其他对象的阻塞导致程序效率低下。public class SingletonLazy { private static SingletonLazy instance null; private SingletonLazy() { } public SingletonLazy getInstance() { synchronized (this) { if (instance null) { instance new SingletonLazy(); } } return instance; } }7.3 处理多个线程访问因为锁对象的阻塞等待问题我们对这个加上判断是否为空的操作来提升效率但是这种如果编译器优化就不知道构造函数这个指令在计算机中的调度是什么的。这三个指令一般是申请内存空间在内存中构造函数(初始化)把内存空间的首地址给引用变量但是编译器可能优化成申请内存空间把内存空间的首地址给引用变量在内存中构造函数(初始化)这时候因为内存可见性问题就是计算机对指令的随机调度问题public class SingletonLazy { private static SingletonLazy instance null; private SingletonLazy() { } public SingletonLazy getInstance() { if(instance null) { synchronized (this) { if (instance null) { instance new SingletonLazy(); } } } return instance; } }7.4 纠正内存可见性问题我们给instance这个变量加上volatile这个变量来保证指令的执行的准确性。确保每次读取操作都是读取内存的值关于该变量的读取和修改操作不会触发重排序问题。这样就写出来了一个可以使用的懒汉模式的代码。public class SingletonLazy { private static volatile SingletonLazy instance null; private SingletonLazy() { } public SingletonLazy getInstance() { if(instance null) { synchronized (this) { if (instance null) { instance new SingletonLazy(); } } } return instance; } }
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门