自己的网站怎么做app,建了个网站百度上会有么,怎么做英文的网站,ps做专业网站一、等待通知
系统内部#xff0c;线程之间是抢占式执行的#xff0c;随即调度#xff0c;程序可以通过手动干预的方式#xff0c;能够让线程一定程度的按咱们想要的顺序执行#xff0c;无法主动让某个线程被调度#xff0c;但可以主动让某个线程等待。等待通知可以安排…一、等待通知
系统内部线程之间是抢占式执行的随即调度程序可以通过手动干预的方式能够让线程一定程度的按咱们想要的顺序执行无法主动让某个线程被调度但可以主动让某个线程等待。等待通知可以安排线程之间的执行顺序。
举个栗子当t1线程要在队列获取元素由于此时队列是空的无法进行工作它只能频繁的进行获取释放锁的操作导致其他线程不能得到cpu分配资源线程中调度是无序的这种情况很可能出现称为——线程饿死不会像死锁那样卡死但是可能会卡一下影响程序效率
等待通知机制可以解决上述问题条件判断是否能执行当前逻辑不能就主动wait阻塞等待把执行的机会让给别的线程避免该线程进行一些无意义的重试等时机成熟时其他线程通知-notify阻塞被唤醒。代码实现
public static void main(String[] args) {Object locker new Object();Thread t1 new Thread(()-{synchronized (locker){System.out.println(t1等待前);try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(t1等待后);});
当我们执行这样的逻辑时线程就会在执行完第一句输出语句后通过wait阻塞等待注意因为wait操作被执行时是先解锁然后阻塞等待解锁的前提是有锁所以需要在操作前先加锁。此时可以通过jconsole来查看线程状态
可以看出此时是WAITING状态。再写另一个线程来唤醒它
Thread t2 new Thread(()-{try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker){System.out.println(t2唤醒前);locker.notify();System.out.println(t2唤醒后);}});
此时工作台可以从输出顺序看到执行过程
1t1线程获取锁 2t1线程阻塞等待且解锁 3t2线程获取锁 4t2线程唤醒t1线程执行完逻辑后释放锁 5t1重新获取锁从上次被阻塞的地方继续执行。t1的状态变化是WAITING-RUNNABLE-BLOCKED 处于blocked状态是因为唤醒后需要等t1先释放锁。
注意notify一次只能唤醒一个线程而且是随机的。不过notify也有可以唤醒所有线程的方法
locker.notifyAll();
wait也有一个带参数的版本无参数版本采用的是死等战术等不到唤醒程序就一直等带参数版本和join差不多过了参数时间就不会再阻塞状态。
二、单例模式
单例模式是一种经典的设计模式相比其他的设计模式算是比较简单的设计模式也是面试中常考的设计模式。
单例模式-单个实例整个进程中有且只有一个对象这样的对象就成为单例instance那么如何保证进程中只有一个实例呢
需要让编译器帮我们进行检查通过编码上的技巧使编译器自动发现我们是否创建了多个实例并尝试创建多个实例时直接编译报错。
单例模式有很多种写法本篇文章主要介绍两种饿汉模式懒汉模式。
1、饿汉模式
先看这样的一串代码
class Singleton{public static Singleton instance new Singleton();
}
static成员初始化时机是在类加载的时候可以简单理解为JVM一启动就立即加载成员也就立即创建了。static修饰的类属性是类对象的每个类的类对象在JVM中只有一个里面的静态成员只有一个初始化也只执行一次当后续需要这个类的实例时可以通过方法来获取已经创建好的实例而不是再创建新的这个方法为 public static Singleton getInstance() {return instance;}
那么如果其他线程想通过此类创建新的对象该怎么办呢?
当类之外的代码想尝试创建新的对象时一定会调用构造方法所以将构造方法的权限设置为private时就会无法调用编译报错。如下
private Singleton(){/ } 当类一加载静态成员就被创建了就像饿的人看见吃的会想赶紧吃的感觉一样所以这种模式可以被称为“饿汉模式”。
2、懒汉模式
在计算机中懒往往是一个褒义词代表着高效率。相对于饿汉模式一加载类就创建对象懒汉则是当第一次需要使用对象才会去创建就把创建实例的代价省下来了按照这个思路来创建类
class SingletonLazy{public static SingleLazy instance null;public static SingleLazy getInstance() {if(instance null){instance new SingleLazy();}return instance;}
}
先将静态成员的引用指向空当需要创建实例时判断当前引用是否为空为空时再创建新的不为空就直接返回实例。懒的本质就是偷懒能少做就少做懒-缓。
如果代码中存在多个单例类都使用懒汉模式的话这些实例会在程序启动时扎堆的创建可能把程序启动时间拖慢如果使用饿汉模式的话调用时机是分散的化整为0让用户感受不到卡顿。
多线程模式下分析懒汉模式与饿汉模式
思考当多个线程同时getInstance时这两种模式是否会引起线程不安全问题
饿汉模式安全但懒汉模式是不安全的。
饿汉模式安全的原因创建实例的时机是java进程启动时比主线程还早创建因此在其他线程调用getInstance时实例肯定已经创建好了每个线程只做了一件事就是读取上述静态变量的值多个线程读取一个变量安全。
而懒汉模式与其不同懒汉模式的关键操作代码是这些
第一行是“读”查看一下实例引用的地址的是否为空而第二行是赋值也就是修改操作上述操作在多线程环境下容易出现问题比如会产生下面这种执行顺序
假定最初instance引用为空t1判断引用为空t2判断引用为空t1创建实例对象由于t2已经判定完是否为空所以也会创建实例对象。
上述代码t2创建的引用会覆盖掉t1的引用的地址进一步t1的instance没有指向了就会被GC回收掉。
解决办法可以通过加锁的方式来保证懒汉模式下getInstance是安全的当t1线程进入判定语句时t2需阻塞等待t1创建完实例释放锁后t2才能获取锁开始判定操作此时的instance就已经指向了地址不为空了。初步优化后的代码
class SingletonLazy{public static SingletonLazy instance null;public static SingletonLazy getInstance() {synchronized (SingletonLazy.class){if(instance null){instance new SingletonLazy();}}return instance;}
}
但是懒汉模式只有在初次调用getInstance时会涉及到线程安全问题一旦实例创建好了后面再调用都是只读操作不涉及线程安全问题而后续调用明明没有线程安全问题还要加锁增加了没必要的开销。
解决办法在加锁前再判断一次当前调用是否为第一次调用如果是第一次调用再去获取锁判定条件还是看instance是否为空即可。
别忘了上篇文章提到的volatile二次优化后的代码
class SingletonLazy{public static volatile SingletonLazy instance null;public static SingletonLazy getInstance() {if(instance null){synchronized (SingletonLazy.class){if(instance null){instance new SingletonLazy();}}}return instance;}
}
通过双重if避免了重复创建对象。 下篇文章更新多线程编程经典案例二——阻塞队列 感谢观看
道阻且长行则将至