spring boot 单例 多线程 知识点

1. spring boot 创建的bean是单例的还是多例的?

单例的,要不然controller类中的非静态变量如何保证是线程安全的

2. 为什么spring要默认是单例呢?

为了性能:单例就不用每次new了

节省资源,因为它控制了实例对象的个数,并有利于gc回收。

其次也不需要多例

因此不要在controller等bean中定义成员变量

3. 服务器是多线程的,为每个用户开辟一个线程,每个用户访问服务器,会创建一个request

4. spring单例模式是指,在内存中只实例化一个类的对象

5. 局部变量不会受多线程影响,成员变量会受到多线程影响

6. JVM是如何实现线程的独立内存空间?
Java中的栈
1、每当启用一个线程时,JVM就为他分配一个Java栈,栈是以帧为单位保存当前线程的运行状态。某个线程正在执行的方法称为当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。当线程执行一个方法时,它会跟踪当前常量池。
2、每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,这个帧自然就成了当前帧。当执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等等。
3、Java栈上的所有数据都是私有的。任何线程都不能访问另一个线程的栈数据。所以我们不用考虑多线程情况下栈数据访问同步的情况

7. Servlet是单实例多线程

8. 多线程安全问题

多线程安全问题:

多线程访问同一资源(同一对象、同一变量...),出现的不同的结果

容易出现多线程安全问题:

线程安全问题大多是由全局变量静态变量引起的,局部变量逃逸也可能导致线程安全问题。

若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;

若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

9. 线程同步作用:

线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。

当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。

线程同步==“排队”

10. 并发与并行

网上找到的一个很好的例子:

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
11. 线程安全与线程不安全

concurrenthashmap:是线程安全的      HashMap是线程不安全的  arrayList是线程不安全的

Vector、HashTable、Properties是线程安全的;

ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等都是线程不安全的。

static HashMap:在进行写操作时才会出现线程不安全

 

知识点:

windows电脑一般最多开个2000个线程

12. synchronized的理解:

来自:https://blog.csdn.net/crazylzxlzx/article/details/52200865

synchronized
在修饰代码块的时候需要一个reference对象作为锁的对象.
在修饰方法的时候默认是当前对象作为锁的对象.
在修饰类时候默认是当前类的Class对象作为锁的对象.

线程同步的方法:sychronized、lock、reentrantLock分析

  • 方法锁(synchronized修饰方法时)

通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。

synchronized 方法控制对类成员变量的访问:
每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突

  • 对象锁(synchronized修饰方法或代码块)

  当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调用者占用,则需要等待此锁被释放。(方法锁也是对象锁)       

java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。 

  • 类锁(synchronized 修饰静态的方法或代码块)

  由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只有一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。  

对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。 

类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。 
java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是[类名.class]的方式。

自我理解(望指点):

方法锁:可将synchronized修饰的代码块或方法姑且理解为此时锁住了这段代码,而该类的实例对象就是钥匙🗝,所以synchronized修饰的代码,仅仅锁了该修饰的代码块,不影响该类的实例对象操作该类的其他方法。例子:

如何达到互斥(线程同步),可理解为一家5口人(5个线程),打开1扇门(synchronized修饰的代码),互斥开启这1扇门,则可持有的钥匙🔑(类的实例对象)仅存在1个,即只存在1把钥匙,则能保证线程同步。

package com.example.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class Test2 implements Runnable{
	static int i = 0;
	 static Test3 test3 = null;

	public static synchronized Test3 getTest3() {
		if (test3 == null){
			test3 = new Test3();
		}
//		System.out.println(test3.toString());
		return test3;
	}

	public static void main(String[] args){
		 ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
	          for(int i = 0; i < 50; i++){
				  Test2 test2 = new Test2();
				  cachedThreadPool.execute(test2);

				  if (i== 8){
					  Test2.getTest3().getI2();
				  }
	          }



	     }

	@Override
	public void run() {

		getTest3().getI();
	}

	private static class Test3{

		public synchronized void getI() {
			try {
				System.out.println("进来了");
				i = i+1;
				Thread.sleep(100);
				System.out.println("释放锁");
				System.out.println(i);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		public void getI2() {
			System.out.println(this);
			System.out.println("能进来嘛,所有:不影响同个实例对象调用其他方法");
		}
	}
}

synchronized:

能够保证在同一时刻最多只有一个线程执行该段代码,易达到保证并发安全的效果

同时访问同步方法和非同步方法,非同步方法不会因为同步方法而受影响

同时访问同一个对象的2个同步方法,是会受影响的,即是串行执行的

同时访问静态synchronized和非静态同步方法,是不受影响,同时进行的


1.线程安全问题都是由全局变量及静态变量引起的。但是,如果每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;如果有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

2.1) 常量始终是线程安全的,因为只存在读操作。 

2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。

3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。


原文链接:https://blog.csdn.net/m0_37771869/article/details/81258188


堆栈内存回收问题:

执行方法体内的代码时,局部变量会保存在JVM栈帧上,方法返回时自动回收但是局部变量指向的对象是保存在JVM堆中的,由垃圾回收器负责回收。
例如:Test s = new Test(); 这里的引用对象s保存在栈上,实际的Test对象保存在堆中。这两个对象都是在执行到这一行代码的时候分配的空间:在字节码中,先new出Test对象,在堆中分配内存,然后使用dup将其引用压入栈帧的操作数栈,然后调用Test对象的构造函数,然后调用astore将此时的栈顶值弹出存到栈帧的局部变量表中去。

基本数据类型就是在栈中的,就是放对象指针的位置改成了数值

其实对比堆和栈,只要把握一个重要的信息,new的对象是存储在堆中的,但为了存取方便,会在栈中声明一个引用变量指向堆中的对象,这样存取方便而且速度快。另一方面,栈中的对象超过作用域即被释放,Java会立即释放掉内存空间另作他用;而堆中即使超出变量的作用范围也不会变成垃圾,只有在没有引用变量指向它时会变成垃圾,但又不会立刻被回收,只有在一个不确定的时间才会被垃圾回收器回收掉,这也就是为什么Java会比较占用内存的原因了。再一点还要注意,就是被static修饰的变量,它是在程序启动之初就会被分配内存,它的生命周期会一直到程序结束,所以使用static一定要慎重。
————————————————
https://blog.csdn.net/rocling/article/details/102790711

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值