当前位置: 首页 > news >正文

购物网站首页设计百度推广落地页

购物网站首页设计,百度推广落地页,网站开发技术路线与规范,广州建设工程交易中心增城电话一、线程安全 1.1 线程安全的概念 线程是随机调度执行的,如果多线程环境下的程序运行的结果符合我们预期则说明线程安全,反之,如果遇到其他结果甚至引起了bug则说明线程不安全 1.2 经典例子与解释 下面举一个经典的线程不安全的例子&…

c95cbc70da094545b55d50d33cc484d4.png


一、线程安全

1.1 线程安全的概念

线程是随机调度执行的,如果多线程环境下的程序运行的结果符合我们预期则说明线程安全,反之,如果遇到其他结果甚至引起了bug则说明线程不安全

1.2 经典例子与解释

下面举一个经典的线程不安全的例子:

public class Demo2 {private static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count++;}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count++;}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = "+ count);}
}

上述代码中t1和t2两个线程对count进行累加操作,在主线程中启动这两个线程然后通过join等待这两个线程都执行完后打印count,预期结果为100000,但打印结果如下:

759abc0e6c384d009e620f0c28defba8.png

上述结果不符合我们的预期,这便是产生了线程安全问题

接下来我们通过CPU指令的方式解释上述原因

count++这个行代码可以看作3个CPU指令:

  1. 把内存count总的值读取到CPU寄存器中 => load
  2. 把寄存器中的值+1,此时任然在寄存器中 =>add
  3. 把上述寄存器计算后的值写回到内存count里 =>save

由于线程随机调度,所以两个线程的CPU指令执行顺序也是随机的。

例如下图:

(画图,时间轴。。。。。。。。。。)

首先t1线程和t2线程分别将1加载到CPU寄存器中(假设此时count的值为1),然后在寄存器中将其加1变为2,最后t1先将2加载回内存中,t2也把2加载回内存中,所以两次加1操作只加了一次1

当然上述执行顺序只是无数可能中的一种,可能t1的一组指令还没有执行完,t2就执行了好几组

下面来总结一下线程不安全的原因

1.3 线程不安全的原因

  1. 线程是随机调度,抢占式执行的
  2. 修改共享数据,多个线程修改同一个变量
  3. 多个线程修改共享数据的操作不是原子性,(count++是3个CPU指令,但是赋值操作就是原子性的)
  4. 内存可见性问题
  5. 指令重排序

4和5后面再解释

1.4 解决线程安全问题

根据上述原因下手

原因1:无法干预

原因2:可以干预,但并不是一个普适的做法,因为有些代码就是要修改同一个变量

原因3:这是一个普适的做法,我们可以将一系列非原子的操作打包成一个原子性的操作->加锁

1.4.1什么是锁

锁是在多线程编程中用来控制线程对共享资源访问的一种机制

1.针对锁主要有这两个操作:

  • 加锁:线程t1加上锁之后,t2也尝试使用同一个锁进行加锁,就会阻塞等待

问:什么叫“t2也尝试使用同一个锁进行加锁”?

答:你可以理解为我们给t2里的操作加上了一种机制,这个机制就是必须加上锁才能进行操作,t1拿了一个锁,加锁后进行它的操作,如果t2也想拿这个锁来加锁就必须等t1操作完成解锁之后,再拿这个锁进行加锁进行它的操作,在此之前t2要阻塞等待,当然,如果t2选择拿别的锁进行加锁就不会阻塞等待(假设只有t1和t2两个线程)

比如,A在餐厅里定了一个包间,把门上锁之后来用餐,这样B来了就不会影响A用餐的过程;也就是t2不会对t1修改count的过程进行干扰,这样就保证了操作的原子性

  • 解锁:t1解锁之后,t2才有可能拿到锁,因为尝试竞争锁的线程可能不只一个

2. 锁的主要特性:互斥,一个线程获取到锁之后,另一个线程也尝试加这个锁,就会阻塞等待,这种现象叫锁竞争锁冲突,代码中也可以有多个锁,只有多个线程竞争同一个锁才会发生锁竞争,竞争不同的锁则不会发生锁竞争

1.5 synchronized关键字

1.5.1 synchronized解读

使用synchronized关键字,synchronized关键字解读:

synchronized (locker) {count++;
}
  1. 这是一个Java的关键字,不是方法
  2. synchronized后面括号里面写的是锁对象
  3. 锁对象的用途:用来区分两个线程是否针对同一个对象加锁,如果是,就会出现锁竞争/互斥就会引起阻塞等待,如果不是就不会出现锁竞争,也就不会阻塞等待
  4. synchronized的{ }:进入到代码块,就是对上述锁对象进行加锁操作,当出了代码块,就是对锁对象进行解锁

我们可以让t1和t2都使用同一个锁对象locker来对count变量的修改操作进行上锁

private static int count = 0;
public static void main(String[] args) throws InterruptedException {Object locker = new Object(); //锁对象Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {synchronized (locker) {count++;}}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {synchronized (locker) {count++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = "+ count);
}

这样,虽然两个线程仍然是抢占式执行的,但是保证了count++;这个操作的原子性,结果为:count = 100000 

Java中随便拿一个对象,都可以作为加锁的对象

 1.5.2 synchronized使用示例

1)修饰代码块:指定锁哪个对象,也就是可以锁任意对象

public class SynchronizedDemo {private Object locker = new Object();public void method() {synchronized (locker) {}}
}

锁当前对象:()里直接写this

public class SynchronizedDemo {public void method() {synchronized (this) {}}
}

2)修饰普通方法:锁的SynchronizedDemo对象,谁调用method()方法,就锁谁(可以有多个)

public class SynchronizedDemo {public synchronized void methond() {}
}

3)修饰静态方法:锁的SynchronizedDemo类对象(一个java进程中,一个类只有唯一一个类对象)

public class SynchronizedDemo {public synchronized static void method() {}
}

1.5.3 synchronized特性

1)互斥

某个线程执行到某个对象的synchronized中时,其他线程如果也执行到同一个对象,synchronized就会阻塞等待

2)可重入

for (int i = 0; i < 50000; i++) {synchronized (locker) {synchronized (locker) {count++;}}
}

上述线程先对locker进行第一次加锁,在第二次加锁的时候,locker对象已经被锁住了,按照之前的理解,尝试针对一个已经被锁的对象加锁时,就会阻塞等待,这种情况就叫死锁

但synchronized是可重入锁,可重入锁的内部包含了线程持有者计数器

  • 如果某个线程加锁的时候,发现这个锁已经被别人占用,但是恰好占用的是自己,那么仍然可以继续获取到锁,并让计数器自增
  • 解锁的时候(也就是每走出一个代码块)计数器就会递减,当减到0时才真正释放锁

这种机制就叫可重入锁

1.6 死锁

1.6.1 两个常见的场景

死锁有两个比较典型的场景

场景一:不可重入锁引起的死锁

一个线程对一个线程连续加锁两次且这个锁是不可重入锁就会引起死锁

场景二:两个线程两把锁

    public static void main(String[] args) throws InterruptedException {Object locker1 = new Object();Object locker2 = new Object();Thread t1 = new Thread(() -> {synchronized(locker1) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2) {System.out.println("t1获取了两把锁");}}});Thread t2 = new Thread(() -> {synchronized(locker2) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker1) {System.out.println("t2获取了两把锁");}}});t1.start();t2.start();System.out.println("死锁ing....");}

上述代码中,locker1被t1占用,locker2被t2占用,接下来t1需要locker2,t2需要locker1,这样就陷入了死锁

运行结果显示程序一直没有结束:

1.6.2 如何避免死锁

死锁产生的四个必要条件:

1)锁具有互斥性

2)锁不可抢占:一个线程拿到锁之后,除非它主动释放锁,否则别人抢不走

以上这两点是锁的基本特性,无法干预

3)请求和保持:一个线程拿到一把锁之后,不释放这个锁的前提下,在尝试获取其他锁(嵌套加锁)

解决方法就是不要让两个sychronized嵌套式的占用两个不同的锁对象进行加锁

4)循环等待:多个线程获取多个锁的过程中,出现了循环等待,A等待B,B又等待A

这一点只要我们提前约定好获取锁的顺序,即使出现了嵌套也不会引起死锁,如下述代码t1和t2线程都先获取locker1再获取locker2,这样就不会出现死锁

    public static void main(String[] args) throws InterruptedException {Object locker1 = new Object();Object locker2 = new Object();Thread t1 = new Thread(() -> {synchronized(locker1) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2) {System.out.println("t1获取了两把锁");}}});Thread t2 = new Thread(() -> {synchronized(locker1) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2) {System.out.println("t2获取了两把锁");}}});t1.start();t2.start();}

任何一个死锁的场景,都必须同时具备上述四点,缺少一点都不会构成死锁


🙉本篇文章到此结束,下篇文章将继续对线程安全的知识进行讲解

http://www.dt0577.cn/news/42430.html

相关文章:

  • 网站建设可以作为无形资产吗玄幻小说百度风云榜
  • 做网站的基本功文明seo技术教程网
  • 酒店 深圳 网站建设百度客服电话24小时客服电话
  • 做网站设计软件网址导航浏览器下载
  • 大渡口集团网站建设seo最好的工具
  • 获取wordpress所有分类名字和idseo关键词推广多少钱
  • 工作室推广网站网络优化培训要多少钱
  • 广州定制网站开发网站建设及推广优化
  • 做橙光游戏的网站热门搜索
  • 济南 网站建设公司 医疗在线生成网页网站
  • 做电脑系统的网站网络营销策略案例
  • 做公司网站教程视频各引擎收录查询
  • 个人网站网页制作焦作网站seo
  • 中国十大企业培训机构排名保定百度seo公司
  • 那个网站可以做ppt赚钱谷歌站长平台
  • 网站跨省备案域名被墙污染查询
  • 哪些公司做网站比较好海外网站
  • 微信商城和微网站建设学网络运营需要多少钱
  • 如何做自己网站的seo活动营销方案
  • 做网站需要画原型图么网站站内推广怎么做
  • 哪个网站做物业贷网络营销最新案例
  • 网站做附近地图导航十大经典营销案例
  • 网站网页设计屏幕尺寸免费代码网站
  • 企业建设网站的主要目的有哪些域名网
  • 接go语言网站开发seo流量软件
  • 章丘区网站建设百度网盘下载app
  • 网站投诉平台免费推广的app有哪些
  • 做模板网站价格天津最新消息今天
  • 天津手机版建站系统哪个好百度快照投诉
  • 网站建设推广工作描述线上宣传有哪些好的方式方法