如何应对并发和并发的可见性与原子性

1.如何应对并发?

  • 假如每秒有10W个请求来访问服务端,如何应对高并发?
  • (1)动静分离
    将js,css,图片,视频等放到CDN上,真正请求后台接口的请求才发到后台
  • (2)水平扩展
    1个机器抗住8W的请求是抗不住的, 利用nginx服务器作为反向代理和负载均衡,将8w分为4w,每台机器应对4w的请求
  • (3)微服务化:将应用进行切分,更细粒度的切分
    假如所有的服务分为30%的物品列表服务,3%的用户详情的服务,67%的交易服务,经过水平扩展后,三种服务存在于每台机器上,处理用户请求的代码存在于各个机器上,占用着资源,却没有收到多少请求,这就是一种资源的浪费。
    微服务化就是不再将完整的服务都部署在一台机器上,而是将三个服务(三块代码,可以互相调用),分别运行,同一台机器可以运行多个相同的服务,eg:交易服务扩展到了4台机器上,一台机器上运行2个交易服务的代码
  • (4)缓存:缓存是在内存中的
    若物品列表的接口一直在DB调用物品详情的信息,DB会从磁盘中读取数据,这样是很慢的。可以在到DB之间加一个redis缓存层(缓存数据库),如果缓存有数据,就从缓存中获取信息
  • (5)秒杀活动:将所有的请求放到服务端的队列中
    在这里插入图片描述

2.并发的可见性

  • 可见性:线程1写操作相应的数据后,线程2的读操作能够读取写操作的最新值
java写的:
bool a=true;
a=false;
system.out.println(a);

eg1:
public class Concurrent2
{
	static boolean a=true;
	public static void main(String [] args) throws InterruptedException
	{
		new Thread(()->//子线程:对a进行读操作
		{
		//子线程对a不可见,a没有读取到最新值,导致一直在这个死循环中,一直读的是CPU的缓存,
			没有读取主存中的最新值
			while(a)
			{
			}
		}).start();
		Thread.sleep(millis:1000);
		a=false;//主线程:对a进行写操作
		System.out.println(a);
	}
}

eg2:
public class Concurrent2
{
	static boolean a=true;
	public static void main(String [] args) throws InterruptedException
	{
		new Thread(()->//子线程:对a进行读操作
		{
			while(a)
			{
				try
				{
					Thread.sleep(millis:1);//给当前的线程一些喘息的时间
				}catch(InterruptedException e)
				{
					e.printStackTracd();
				}
			}
		}).start();
		Thread.sleep(millis:1000);
		a=false;//主线程:对a进行写操作
		System.out.println(a);
	}
}
  • 内存条称之为主存,
    (1)local cache指的就是CPU的多级缓存
    L1缓存:每个CPU核心都有一个
    L2缓存:可能是两个CPU核心共用一个
    L3缓存:可能是四个或者8个CPU核心共用一个
    (2)core代表:CPU的核心
    (3)虽然线程1先写(主线程),线程2读(子线程),但是线程2不能够立即读取到线程1写的最新值,也称之为java中的不可见性。
    (4)结合上面eg2的代码和下面的图片,假设前面的cpu核心执行的是主线程,后面的cpu核心执行的是子线程while,虽然主线程将a的值刷回内存,其值为false,但是while线程不能立即读取到主存中发生的变化,一直在这里死循环,一直从local cache读取值。
    while中加sleep的作用是:给子线程一定的休息时间,让他从主存中刷新a的值,那么就不会一直死循环了。
    在这里插入图片描述

  • volatile作用:在主存和local cache之间保持了强制一致性

public class Concurrent2
{
	static volatile boolean a=true;//volatile 使得a在主存和local cache之间是强制刷新的
	public static void main(String [] args) throws InterruptedException
	{
		new Thread(()->//子线程:对a进行读操作
		{
			while(a)
			{
			}
		}).start();
		Thread.sleep(millis:1000);
		a=false;//主线程:对a进行写操作
		System.out.println(a);
	}
}

3.并发的原子性

  • 可见性只能保证一个线程写,另一个线程读是正确的,这就是volatile的用处。
  • 两个线程既要读,又有基于读操作的写操作,这个时候就不是volatile的作用了。
    eg:下图的i++操作:i++操作可以分为:(1)读i,(2)i++,(3)给i赋值
    T1表示线程1,T2表示线程2,2个线程都要经过上述的步骤,都要先读i
    2个线程读的i值为0,自增后,T1的i值为1,T2的i值为1,最终刷到主存中后i的值为1
    在这里插入图片描述
    若i的类型是volatile的,T1自增后的i值,会强制刷新到主存中,此时主存中的i值=1,但是
    即使主存中i的值已经是1了,但是T2也不会去读,因为T2已经读取过i的值了
    在这里插入图片描述
    用volatile编写代码后发现,最后的值不是20000
    在这里插入图片描述
  • 原子性:要么读写同时发生,要么读写同时不发生
    java中的AtomicInteger能够保证原子性

public class Concurrent2
{
	static AtomicInteger a=new AtomicInteger(initialValue:0);//要么这一步执行,要么这一步执行完了
	public static void main(String [] args) throws InterruptedException
	{
		for (int i=0;i<10000;i++)
		{
			new Thread(()->{
			a.getAndAdd(delta:1);
			}).start();
		
			new Thread(()->{
			a.getAndAdd(delta:1);
			}).start();

		}
		Thread.sleep(millis:1000);
		a=false;//主线程:对a进行写操作
		System.out.println(a);
	}
}
  • 可见性与原子性的关系:在保证原子性之前,要保证可见性。eg:Java中的AtomicInteger类中的值本身就是volatile类型的。
    volatile不具有传染性,用volatile修饰的对象的内部属性不具有可见性,反之用volatile修饰的内部属性也不能保证所在对象的可见性。(java)

  • 同步关键字synchronized:同一时间只有一个线程进入代码段,强制保证原子性

public class Concurrent2
{
	static AtomicInteger a=new AtomicInteger(initialValue:0);//要么这一步执行,要么这一步执行完了
	static int b=0;
	public static void main(String [] args) throws InterruptedException
	{
		for (int i=0;i<10000;i++)
		{
			new Thread(()->{
			//a.getAndAdd(delta:1);
			synchronized (Concurrent2.class){
				b++;
			}
			}).start();
		
			new Thread(()->{
			//a.getAndAdd(delta:1);
			synchronized (Concurrent2.class){
				b++;
			}
			}).start();

		}
		Thread.sleep(millis:1000);
		a=false;//主线程:对a进行写操作
		System.out.println(a);
	}
}

最后的结果为20000
  • volatile是轻量级的,AtomicInteger是基于volatile的,synchronized是更加重量级的
    (1)volatile能够保证一个线程写,另一个线程读的话,能够读取到最新的值,补充:还有防止指令重拍的作用
    (2)AtomicInteger保证既有写操作,又有读操作,eg:i++,保证操作的原子性
    (3)synchronized能够保证代码块中的操作都是原子的

参考:

https://www.bilibili.com/video/av66094038
发布了582 篇原创文章 · 获赞 143 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u011436427/article/details/104343896