线程安全
线程可能会出现这些情况
导致两个线程不能达到自己想要去循环的次数,可能两个线程各10000,那么他们就会出现不到5000甚至不到5000的情况。
出现线程的不安全原因:
1.线程在系统中是随机调度,抢占式执行的.[线程不安全的, 罪魁祸首,万恶之源]。
无法改变!
2.当前代码中, 多个线程同时修改同一个变量
一个线程修改同一个变量 =>没事.
多个线程读取同一个变量 =>没事. 每次读到的结果都是固定的
多个线程修改不同的变量 =>没事.你改你的,我改我的,不影响
3.线程针对变量的修改操作,不是"原子"的
不可拆分的最小单位。
4.内存可见性问题,引起的线程不安全
5.指令重排序, 引起的线程不安全
解决问题
原因1.无法用实际行动干预,除非重写源代码才可。
原因2,是一个切入点,但是在 java 中,这种做法,不是很普适,只是针对一些特定场景是可以做到的
String 是 不可变 对象
java 标准库把 String 设计成不可变对象原因:
1.很好的保证线程安全
2.有稳定的 hash 值
3.方便在常量池中缓存.
原因3,这是解决线程安全问题,最普适的方案.
可以通过一些操作,把上述一系列“非原子"的操作,打包成一个"原子"操作.
关于锁,主要的操作是两个方面
1)加锁 t1 加上锁之后, t2 也尝试进行加锁,就会阻塞等待 (都是系统内核控制)(在java 中就能看到 BLOCKED 状态)
2)解锁 直到 t1 解锁了之后, t2 才有可能拿到锁(加锁成功)
举个例子
一个坑,两个人上,当一个人占坑的时候==加锁,等这个人上完了=解锁。这时候第二个人才可能有机会上厕所,当出现第三个人的时候,第二个人会和第三个人一起枪锁。
如果你两个线程,针对不同的坑位加锁,不会产生互斥的(也称为 锁竞争/锁冲突)
只有是两个线程(人)针对同一个坑位加锁,才有互斥~~
锁的小结:
1.锁涉及到两个核心操作
加锁 解锁
2.锁的主要特性: 互斥
一个线程获取到锁之后,另一个线程也尝试加这个锁,就会阻塞等待,(也叫做锁竞争/锁冲突)
3. 代码中, 可以创建出多个锁.
只有多个线程竞争同一把锁,才会产生互斥,针对不同的锁, 则不会
Java 中随便拿一个对象, 都可以作为加锁的对象.(这个是 Java 中特立独行的设定)其他语言中(C++,Go, Python....) 都是只有极少数特定的对象可以用来加锁.
synchronized(锁)
synchronized后面带上()里面就是写的“锁对象
synchronized下面跟着{}当进入到代码块,就是给上述()锁对象进行了加锁操作当出了代码块,就是给上述()锁对象进行了解锁操作看不到形如lock()unlock0方法的(其他语言都是这么搞的)
public class Test {public static int count;public static void main(String[] args) throws InterruptedException {Object o=new Object();Thread t1=new Thread(()->{for (int i=0;i<50000;i++){synchronized (o){count++;}}});Thread t2=new Thread(()->{for (int i=0;i<50000;i++){synchronized (o){count++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(count + "count");}
}
针对同一个对象加锁后count==100000,如果synchronized锁住的东西不同,则会不会产生互斥现象。count!=100000
synchronized (o)放在for循环外面就会降低效率。
synchronized (this)这样的时候所指的就是调用的对象。
还有一个特殊的是static方法.没有this!
synchronized static void func(){
}
synchronized修饰static方法相当于针对该类的类对象加锁~
27