定制网站多少钱,陕西省网页制作,推荐邯郸网站建设,阿里云可以做电影网站吗阿华代码#xff0c;不是逆风#xff0c;就是我疯#xff0c;你们的点赞收藏是我前进最大的动力#xff01;#xff01;希望本文内容能够帮助到你#xff01;
目录 一#xff1a;问题引入
二#xff1a;问题深入
1#xff1a;举例说明
2#xff1a;图解双线程计算…阿华代码不是逆风就是我疯你们的点赞收藏是我前进最大的动力希望本文内容能够帮助到你
目录 一问题引入
二问题深入
1举例说明
2图解双线程计算
编辑
3线程不安全原因的总结
1根本原因
2代码结构
3直接原因
4内存可见性问题
5指令重排序问题
5解决问题的思路
1针对根本原因解决
2针对代码结构的原因解决
3针对直接原因——加锁
三synchronized关键字加锁
1synchronized
2核心内容
1含义
2代码解释
①“锁竞争”
②“加锁”
③“同一对象”
④“都要加锁”
3变式
变式①this
变式② 类名.class
变式③public前加synchronized
变式④static方法前加synchronized少见 一问题引入
用多线程让计数器自增到1_0000
package thread;public class ThreadDemon19 {private static long count 0;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(()-{for (int i 0; i 5000; i) {count ;}});Thread t2 new Thread(()-{for (int i 0; i 5000; i) {count ;}});t1.start();t2.start();t1.join();t2.join();System.out.println(双线程的计算结果是count);}}package thread;public class ThreadDemon20 {private static long count 0;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(()-{for (int i 0; i 1_0000; i) {count ;}});t1.start();t1.join();System.out.println(单线程的计算结果是count);}}通过上述代码的举例我们发现解决同一个问题怎么最后的结果会不一样呢真是奇了怪了。
二问题深入
结果不一样猜测是循环自增代码这一块出现问题
1举例说明
我们知道cpu可以读取指令解析指令执行指令此处我们重点关注执行指令
count实际由三个指令构成的
1load从内存中读取数据到cpu的寄存器当中
2add把寄存器当中的值1
3save把寄存器当中的值写回到内存中
2图解双线程计算 t1t2双线程的运行下可能同一次读取操作中t1和t2都读取到的是没有自增的数 可以通俗的理解本来t1由数字1自增后到2t2读取的应该是2然后自增到3. 但是如果t2 在 t1把自增后的2 save回寄存器中 之前 读取的话 t2读到的就是1最后只能自增到2 可以理解成被覆盖了 所以这就出现了矛盾计算的数据越小矛盾越小因为cpu运行速度很快可能第一个线程运行结束了第二个线程还没有开始运行 以上可以画出无数种情况比如t1线程自增了2次3次t2线程才执行了1次。这就是线程的随机调度和抢占式执行
3线程不安全原因的总结
1根本原因
是线程的“抢占式执行和随机调度”
2代码结构
多个线程可以修改同一变量
3直接原因
是上述多线程修改变量这一操作并不是“原子性”的而是可拆分的就像我们上面画的图这里就是操作系统底层结构的问题了
4内存可见性问题
5指令重排序问题
45条上述代码没有涉及我们后续再详细引入
5解决问题的思路
为了确保结果的正确我们得确保第一个线程save了过后第二个线程再去load。这时第二个线程load到的数据才是自增过后正确的数据
1针对根本原因解决
不可行。如果要修改线程的“抢占式执行和随机调度”这一机制的话就得修改操作系统中的内核相当于是重新写了一个“新的系统”
2针对代码结构的原因解决
有些地方代码结构可以进行修改但是有些地方不可以视情况而论
3针对直接原因——加锁
count由三个指令完成我们如果给这三个指令打包成一个整体的话就可以避免线程出现问题了也就是“加锁”
三synchronized关键字加锁
1synchronized
关键字synchronized(对象){加锁内容};
注synchronized的加锁依赖于对象
2核心内容
1含义
如果第一个线程对某个对象上锁之后第二个线程要想对同一个对象上锁就必须等第一个线程释放锁此时第二个线程是处于BLOCKED阻塞状态
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-21* Time: 15:27*/
public class ThreadDemon21 {private static long count 0;public static void main(String[] args) throws InterruptedException {Object locker new Object();//创建一个object对象Thread t1 new Thread(()-{for (int i 0; i 5000; i) {synchronized (locker){ //锁到object这个对象上count;}}});Thread t2 new Thread(()-{for (int i 0; i 5000; i) {synchronized(locker){ //锁到object这个对象上count;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(计算结果是 count);}}2代码解释 ①“锁竞争”
通过“锁竞争”让让第二个线程无法插入到第一个线程的执行指令当中。
②“加锁”
“加锁”就是把count中三个指令loadaddsave打包成一个“原子性”的操作最小单位的操作再也不可分割了
③“同一对象”
加锁的对象不同“锁竞争”就不会发生线程安全问题依旧存在
④“都要加锁”
如果第一个线程加锁第二个线程不加锁“锁竞争”也不会发生线程安全问题依旧存在
3变式
变式①给this加锁
this指向的还是同一个对象t1和t2依旧会产生“锁竞争”
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-21* Time: 15:27*/
class Test{public long count 0;public void add(){synchronized (this){count;}}}
public class ThreadDemon22 {public static void main(String[] args) throws InterruptedException {Test test new Test();Thread t1 new Thread(()-{for (int i 0; i 5000; i) {test.add();}});Thread t2 new Thread(()-{for (int i 0; i 5000; i) {test.add();}});t1.start();t2.start();t1.join();t2.join();System.out.println(计算结果是 test.count);}
}变式② 类名.class
获取到当前类的对象类对象包含了这个类的各种信息类名字属性方法....... 变式③public前加synchronized
等价写法 变式④给类对象加锁static
static方法前加synchronized少见 如果synchronized加到static方法上就相当于给类对象加锁了