单例
- 单例:在一个java进程中保存一个实例
饿汉----- 不能按需加载,可能会造成资源浪费
/**
* 饿汉----- 不能按需加载,可能会造成资源浪费
*/
public class Singleton01 {
private static final Singleton01 SINGLETON_01 = new Singleton01();
private Singleton01(){}
private static Singleton01 getSingleton01(){
return SINGLETON_01;
}
}
懒汉----判断一下,用到的时候在创建,延时加载
/**
* 懒汉----延迟加载
*/
public class Singleton02 {
private static Singleton02 SINGLETON_01;
private Singleton02(){}
private static Singleton02 getSingleton01(){
if (SINGLETON_01 == null)SINGLETON_01 = new Singleton02();
return SINGLETON_01;
}
}
线程问题。加锁
synchronizaed---效率问题(这里只有null才用等待,但是synchronizaed是全等待)
/**
* 懒汉--- 在方法上加synchronized ,可以保证线程安全,但是效率是很大问题
* 因为下方代码,只有为null的时候才有必要等待,但是这样任何情况都要等待,锁太多了
*/
public class Singleton03 {
private static Singleton03 SINGLETON_01;
private Singleton03(){}
private static synchronized Singleton03 getSingleton01(){
if (SINGLETON_01 == null)SINGLETON_01 = new Singleton03();
return SINGLETON_01;
}
}
-
这里我们未解决上面问题,把锁的位置进行改变,但是并不完美,还是有可能出问题
-
最后衍生出这种比较好的方式。接着往下看
目前存在的问题:
1.问题一:指令重排序(JVM会在不影响代码最终逻辑的情况下,按照自己最优的方式执行)
2.问题二(JMM内存模型,引起线程可见性)
- 解决问题: 加关键字 volatile
1.防止指令重排序(某个变量加了volatile,就可以保证前面代码一定执行结束在执行他,他之后的代码一定是在他之后执行)
2.保证线程可见性 (线程A 进行修改,就会强制使线程B实现【必须要再次从主存中读取】线程B改变了,线程A或者其他的都失效了)
3.不保证原子性(下面图片)
public class VoilatileTest {
private static volatile int i =0;
private static void incr (){
i++;
}
public static void test(){
for (int j=0;j<100;j++){
new Thread(new Runnable() {
@Override
public void run() {
for (int k=0;k<100;k++)incr();
}
}).start();
}
}
public static void main(String[] args) {
test();
//当前存活的线程 除了主线程
// 精灵线程(守护线程):随着守护的线程出生而出生,随着守护的线程死亡而死亡 垃圾回收
//在正常结束程序finally是否一定执行,不一定,在守护线程中
while (Thread.activeCount()>2){
//礼让 让别的线程先执行
Thread.yield();
}
System.out.println(i);
}
}
运行结果是不定的,可能是10000,也可能是9998,9999,9993,9994等等。原因就是volatile不能保证原子性
所以就没有什么完美的解决方案,都是在项目中针对情况而定。需要哪种方式就用哪种。
Thread.currentThread()
--- 获取当前线程
- 在正常结束程序 finally 是否一定执行?【不一定,在守护线程中】
volatile与synchronized的区别
- volatile可以解决可见性、防止指令重排序,但是有缺点就是不保证原子性,在高并发的情况下,会出现错误,使用时要针对情况来定是否使用
- synchronized很强大,可以解决volatile的不保证原子性问题,但是效率很低。锁都可以解决,只是考虑效率问题,使用也是看情况定
动态代理
- 1.通过 jdk实现
- 2.通过cglib实现
spring两种都有,如果有接口就用jdk,如果没有就用cglib。也可以强制使用cglib
/**
* 实现了InvocationHandler 接口的类才有jdk代理功能
*/
public class DaiLiProxy implements InvocationHandler{
private DaiLiTest daiLiTest;
public DaiLiProxy( DaiLiTest daiLiTest){
this.daiLiTest = daiLiTest;
}
public Object getInstance(){
//类加载器 就用被代理对象的类加载器
//被代理对象实现接口
//能实现代理功能的对象
return Proxy.newProxyInstance(daiLiTest.getClass().getClassLoader(),
daiLiTest.getClass().getInterfaces(),
this);
}
//method就代表你调用的方法 在我们这儿就代表show方法
//proxy代表的就是代理对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke方法被调用了");
System.out.println("开启事务");
try {
//真正执行被代理对象的方法
method.invoke(daiLiTest, null);
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
}
return null;
}
public static void main(String[] args) {
//被代理对象
DaiLiTest daiLiTest = new DaiLiTest();
DaiLiProxy daiLiProxy = new DaiLiProxy(daiLiTest);
//获得代理对象
IDaiLi instance = (IDaiLi)daiLiProxy.getInstance();
instance.show();
}
}
public interface IDaiLi {
void show();
}
public class DaiLiTest implements IDaiLi{
public void show(){
System.out.println("明天休息");
throw new RuntimeException("出现异常");//不加这异常,得到结果图一。加上的到结果图二
}
}
图一
图二
工厂模式
反射 -- Reflect
动态的时候可以获取他的信息,进行修改
1.获得class对象(3种)
实体类.class
new 实体类().getClass();
Class.forName("实体类完全限定名");
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnno {
String value() default "";
}
@TestAnno("中午吃什么啊?")
public class Student {
private String name;
public Student(String name) {this.name = name;}
public Student(){}
public String getName() {return name;}
private String show(String sos,int i){
System.out.println(name+"show一波:"+sos+" "+i);
return "返回值呗拿到了";
}
}
public class ReflectTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class<Student> studentClass = Student.class;//1.获得class对象
Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor();//获得构造方法
declaredConstructor.setAccessible(true);//无论是属性,构造方法,方法 只要是私有都需要设置可进入
Student student = declaredConstructor.newInstance();//创建student对象
System.out.println(student);//reflect.Student@1540e19d
TestAnno annotation = studentClass.getAnnotation(TestAnno.class);//获得单个注解
String value = annotation.value();
System.out.println(value);//中午吃什么啊?
//获得所有注解
Annotation[] annotations = studentClass.getAnnotations();
for (Annotation annotation1 : annotations) {
System.out.println(annotation1);//@reflect.TestAnno(value=中午吃什么啊?)
}
//判断是否有某个注解
boolean annotationPresent = studentClass.isAnnotationPresent(TestAnno.class);
System.out.println(annotationPresent);//true
//获得属性
Field name = studentClass.getDeclaredField("name");
name.setAccessible(true);
//改值 肯定得依赖对象存在
name.set(student,"abc");
System.out.println(student.getName());//abc
//获得方法
Method method = studentClass.getDeclaredMethod("show", String.class, int.class);
method.setAccessible(true);
Object result = method.invoke(student, "跳舞", 10);
System.out.println(result);//abcshow一波:跳舞 10 //返回值呗拿到了
}
}
2.获得构造方法
``
3.获得注解
4.获的属性
懒汉防止不了反射攻击,懒汉final修饰,反射还是可以改值。
饿汉就可以防止反射,饿汉加上final修饰,反射就攻击不了。