同步锁
-
如何判断程序有没有可能出现线程安全问题,主要有以下三个条件:
在多线程程序中 + 有共享数据 + 多条语句操作共享数据
解决思路:
可以从第3点"多条语句操作共享数据"入手,既然是在这多条语句操作数据过程中出现了问题
那我们可以把有可能出现问题的代码都包裹起来,一次只让一个线程来执行 -
同步与异步
-
同步:体现了排队的效果,同一时刻只能有一个线程独占资源,其他没有权利的线程排队。
坏处就是效率会降低,不过保证了安全。
-
异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢占资源。
坏处就是有安全隐患,效率要高一些。
-
synchronized同步关键字
-
写法:
synchronized (锁对象){undefined
需要同步的代码(也就是可能出现问题的操作共享数据的多条语句);
} -
前提
同步效果的使用有两个前提:
- 前提1:同步需要两个或者两个以上的线程(单线程无需考虑多线程安全问题)
- 前提2:多个线程间必须使用同一个锁(我上锁后其他人也能看到这个锁,不然我的锁锁不住其他人,就没有了上锁的效果)
-
特点:
- synchronized同步关键字可以用来修饰代码块,称为同步代码块,使用的锁对象类型任意,但注意:必须唯一!
- synchronized同步关键字可以用来修饰方法,称为同步方法
- 同步的缺点是会降低程序的执行效率,但我们为了保证线程的安全,有些性能是必须要牺牲的
- 但是为了性能,加锁的范围需要控制好,比如我们不需要给整个商场加锁,试衣间加锁就可以了
-
解决超卖—在循环语句里添加双重检测
双重检测 :入口加判断 if(可以进入的条件){共享的多条语句}
出口加判断—即为while循环的出口语句 if(结束执行的条件){break;}
if(进入的条件){共享语句} if(结束执行条件)break;
-
解决重卖----同步代码块—在循环里
方法1:
-
创建唯一的锁对象----成员变量,如果是继承类需要加静态
-
解决的是重卖的问题 用synchronized () {}
-
创建锁对象---new一个 比如:Cat c = new Cat(); while(true){ synchronized (锁对象) { 双重检测 + 共享语句 }
方法2:
- 类名.class 字节码文件对象是唯一的锁对象
while(true){ synchronized (类名.class) { 双重检测 + 共享语句 }
备注:
方法1--------实现类用的多
方法2--------继承类用的多
-
-
线程池
注解
-
分类
- JDK自带注解
- 元注解
- 自定义注解
-
JDK自带注解
JDK注解的注解,就5个:
- @Override :用来标识重写方法
- @Deprecated标记就表明这个方法已经过时了,但我就要用,别提示我过期
- @SuppressWarnings(“deprecation”) 忽略警告
- @SafeVarargs jdk1.7出现,堆污染,不常用
- @FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
-
元注解
用来描述注解的注解,就5个:- @Target 注解用在哪里:类上、方法上、属性上等等
- @Retention 注解的生命周期:源文件中、字节码文件中、运行中
- @Inherited 允许子注解继承
- @Documented 生成javadoc时会包含注解,不常用
- @Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用
-
自定义注解
package cn.tedu.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** 四 本类用于完成自定义注解*/ public class TestAnnotation { } //2.通过 @Target 注解标记自定义注解可以使用的位置,要写在自定义注解的上边 //@Target(ElementType.METHOD)//小括号里相当于方法的参数 现在这个表示 注解只能加在方法上 /*3.通过元注解 @Target 规定自定义注解可以使用的位置 * 我们使用 "ElementType.静态常量" 的方式来指定自定义注解具体可以家在什么位置 * 而且,值可以写多个,格式:@Target({ElementType.xxx1,ElementType.xxx2})*/ @Target({ElementType.METHOD,ElementType.TYPE})//规定多个位置 //3.通过@Retention注解标记自定义注解的生命周期 /* 4. 通过元注解@Retention 规定自定义注解的声明周期 * 我们使用 "RetentionPolicy.静态常量" 的方式来指定自定义注解的生命周期 * 注意:值只能写一个:SOURCE CLASS RUNTIME 3选1*/ @Retention(RetentionPolicy.RUNTIME) //1.定义自定义注解 /*1.首先注意:注解定义的语法与java不同, * 2.定义自定义注解的格式: @interface + 注解名 * */ @interface Rice{ //5.我们可以给注解进行工能增强 ---- 添加注解的属性 /*6. int age(); 不是方法的定义,而是给自定义注解添加了一个age属性*/ // int age();//给自定义注解添加了一个普通属性age 类型为int int age() default 0;//给自定义注解的普通属性赋予默认值0 /*7.注解中还可以添加特殊属性value, *特殊属性的定义方式和普通属性一样,主要是使用方式不同 *注意:特殊属性的名字必须叫value,但是类型不做限制 *特殊属性也可以赋予默认值,格式与普通属性的赋值一样,不能简写 * */ //String value();//定义一个特殊属性 value,类型是String String value() default "Lemon";//定义特殊属性 并且给特殊属性赋予默认值 } /*5.注解使用的格式:@注解名*/ //4.定义一个类 用来测试自定义注解 //@Rice class TestAnno{ /*测试1:分别给TestAnno类 name属性 eat方法上都添加Rice注解 * 结论: 属性上的注解报错了,说明自定义的注解可以加在什么位置,由@Target决定*/ // @Rice 报错了 String name; /*测试2 : 当我们给Rice注解添加了一个属性age以后,@Rice注解使用时会直接报错 * 结论: 当注解没有定义属性时,可以直接使用 * 当注解定义了属性以后 必须给属性赋值,格式:@Rice(age = 10) 必须写全不能简写*/ // @Rice(age=10) /*测试3 : 给age属性赋予默认值以后 可以直接使用@Rice注解 * 不需要给age属性赋值 因为age属性已经有默认值0了*/ //@Rice报错 /*测试4.给Rice注解添加了特殊属性value后,也必须给属性赋值 * 只不过特殊属性赋值时可以简写成: @Rice("Apple")*/ /*测试5. 如果特殊属性也赋予了默认值,那么可以直接使用这个注解 * 如果想要给注解的所有属性赋值,每条赋值都不能简写*/ //@Rice // @Rice("Apple") @Rice(age=10,value="orange") public void eat(){ System.out.println("eat方法"); } }
单例程序
单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。
package cn.tedu.design; /** 五 本类用于实现单例设计模式 方案1 饿汉式*/ public class Singleton1 { public static void main(String[] args) { // new MySingle(); MySingle s1 = MySingle.getSingle(); MySingle s2 = MySingle.getSingle(); System.out.println(s1==s2);//true System.out.println(s1); System.out.println(s2); } } //0.创建自己的单例程序 class MySingle{ //1.提供本类的构造方法 并将其私有化 /*1.构造方法私有化的目的:为了防止外界随意调用,创建本类对象*/ private MySingle(){ } //2.创建本类对象 并将其私有化 //4.2 由于静态资源只能调用静态资源 所以single对象也需要设置为静态 //5.为什么加私有 是因为避免在主方法中被类名直接调用属性值 private static MySingle single = new MySingle(); //3.提供公共的访问方式,返回刚刚创建好的对象 //4.1 为了不通过对象 直接调用本方法 需要将本方法设置为静态 public static MySingle getSingle(){ return single; } }