综合知识查缺补漏

线程相关

1、线程局部变量 ThreadLocal

ThreadLocal 的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个
线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。

  • 每个线程调用全局 ThreadLocal 对象的 set 方法,在 set 方法中,首先根据当前线程获取当前线程的ThreadLocalMap 对象,然后往这个 map 中插入一条记录,key 其实是 ThreadLocal 对象,value 是各自的 set方法传进去的值。也就是每个线程其实都有一份自己独享的 ThreadLocalMap对象,该对象的 Key 是 ThreadLocal对象,值是用户设置的具体值。在线程结束时可以调用 ThreadLocal.remove()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的 ThreadLocal 变量。
  • ThreadLocal 的应用场景:
    ➢ 订单处理包含一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码
    分别位于不同的模块类中。
    ➢ 银行转账包含一系列操作: 把转出帐户的余额减少,把转入帐户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的帐户对象的方法。

2. 在 java 中 wait 和 sleep 方法的不同?

最大的不同是在等待时 wait 会释放锁,而 sleep 一直持有锁。wait 通常被用于线程间交互,sleep 通常被用于暂停执行。

3. synchronized 和 volatile 关键字的作用

一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
1.volatile 仅能使用在变量级别;
synchronized 则可以使用在变量、方法、和类级别的
2.volatile 仅能实现变量的修改可见性,并不能保证原子性;
synchronized 则可以保证变量的修改可见性和原子性
3.volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
4.volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化

4、对线程池的理解(如果问到了这样的问题,可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略)

好处:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

5、线程池的启动策略

当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
c. 如果这时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么还是要创建线程运行这个任务;
d. 如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小
拓展知识:

	corePoolSize 线程池基本大小
	maximumPoolSize 线程池最大数
	poolSize: 当前线程数
	keepAliveTime 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间

6、如何控制某个方法允许并发访问线程的个数?

public class SemaphoreTest {
10. /*
11. * permits the initial number of permits available. This value may be negative,
12. in which case releases must occur before any acquires will be granted.
13. fair true if this semaphore will guarantee first-in first-out granting of
14. permits under contention, else false
15. */
16. static Semaphore semaphore = new Semaphore(5,true);
17. public static void main(String[] args) {
18. for(int i=0;i<100;i++){
19. new Thread(new Runnable() {
20.
21. @Override
22. public void run() {
23. test();
24. }
25. }).start();
26. }
27.
28. }
29.
30. public static void test(){
31. try {
32. //申请一个请求
33. semaphore.acquire();
34. } catch (InterruptedException e1) {
35. e1.printStackTrace();
36. }
37. System.out.println(Thread.currentThread().getName()+"进来了");
38. try {
39. Thread.sleep(1000);
40. } catch (InterruptedException e) {
41. e.printStackTrace();
42. }
43. System.out.println(Thread.currentThread().getName()+"走了");
44. //释放一个请求
45. semaphore.release();
46. }
47. }
可以使用 Semaphore 控制,第 16 行的构造函数创建了一个 Semaphore 对象,并且初始化了 5 个信号。
这样的效果是控件 test 方法最多只能有 5 个线程并发访问,对于 5 个线程时就排队等待,走一个来一下。
第 33 行,请求一个信号(消费一个信号),如果信号被用完了则等待,第 45 行释放一个信号,释放的信号新的线程就可以使用了。

补充:

Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore的主要方法摘要:
		void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
        void release():释放一个许可,将其返回给信号量。
创建Semaphore时候参数的个数就是初始化许可证的个数、
例如:Semaphore s1=new Semaphore(3) 初始化许可证的个数是三个

7、三个线程 a、b、c 并发运行,b,c 需要 a 线程的数据怎么实现

. public class ThreadCommunication {
 1. private static int num;
 2. /**
 3. * 定义一个信号量,该类内部维持了多个线程锁,可以阻塞多个线程,释放多个线程,
 4. 线程的阻塞和释放是通过 permit 概念来实现的
 5. * 线程通过 semaphore.acquire()方法获取 permit,如果当前 semaphore 有 permit 则分配给该线程,
 6. 如果没有则阻塞该线程直到 semaphore
 7. * 调用 release()方法释放 permit。
 8. * 构造函数中参数:permit(允许) 个数,
 9. */
感恩于心,回报于行。 面试宝典系列-Java
http://www.itheima.com Copyright© 2018 黑马程序员
258
 10. private static Semaphore semaphore = new Semaphore(0);
 11. public static void main(String[] args) {
 12.  13. Thread threadA = new Thread(new Runnable() {
 14.  15. @Override
 16. public void run() {
 17. try {
 18. //模拟耗时操作之后初始化变量 num
 19. Thread.sleep(1000);
 20. num = 1;
 21. //初始化完参数后释放两个 permit
 22. semaphore.release(2);
 23.  24. } catch (InterruptedException e) {
 25. e.printStackTrace();
 26. }
 27. }
 28. });
 29. Thread threadB = new Thread(new Runnable() {
 30.  31. @Override
 32. public void run() {
 33. try {
 34. //获取 permit,如果 semaphore 没有可用的 permit 则等待,如果有则消耗一个
 35. semaphore.acquire();
 36. } catch (InterruptedException e) {
 37. e.printStackTrace();
 38. }
 39. System.out.println(Thread.currentThread().getName()+"获取到 num 的值为:"+num);
 40. }
 41. });
 42. Thread threadC = new Thread(new Runnable() {
 43.  44. @Override
 45. public void run() {
 46. try {
 47. //获取 permit,如果 semaphore 没有可用的 permit 则等待,如果有则消耗一个
 48. semaphore.acquire();
 49. } catch (InterruptedException e) {
 50. e.printStackTrace();
 51. }
 52. System.out.println(Thread.currentThread().getName()+"获取到 num 的值为:"+num);
 53. }
 54. });
 55. //同时开启 3 个线程
 56. threadA.start();
 57. threadB.start();
 58. threadC.start();
 59.  60. }
 61. }

8、线程产生死锁的必要条件

1、互斥条件
2、不可剥夺条件
3、请求和保持
4、循环等待

9、多线程之间如何实现通信

 1、共享变量
 2、wait/notify机制

JavaSE高级知识

java中的设计模式

在这里插入图片描述
在这里插入图片描述

1、 单例模式

		  饿汉式:
    1. public class Singleton {
	2. // 直接创建对象
	3. public static Singleton instance = new Singleton();
	4.
	5. // 私有化构造函数
	6. private Singleton() {
	7. }
	8.
	9. // 返回对象实例
	10. public static Singleton getInstance() {
	11. return instance;
	12. }
	13. }
		 懒汉式:
    1. public class Singleton {
	2. // 声明变量
	3. private static volatile Singleton singleton = null;
	4.
	5. // 私有构造函数
	6. private Singleton() {
	7. }
	8.
	9. // 提供对外方法
	10. public static Singleton getInstance() {
	11. if (singleton == null) {
	12. synchronized (Singleton.class) {
	13. if (singleton == null) {
	14. singleton = new Singleton();
	
	15. }
	16. }
	17. }
	18. return singleton;
	19. }
	20. }

2.工厂设计模式

  • 工厂方法模式

工厂方法模式分为三种:普通工厂模式,就是建立一个工厂类,对实现 了同一接口的一些类进行实例的创建。多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
(1)普通工厂模式
(2)多个工厂模式
(3)静态工厂模式

  • 抽象工厂模式

     工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,
     这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,
     创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
    
1. public interface Provider {
2. public Sender produce();
3. }
4. -------------------------------------------------------------------------------------
5. public interface Sender {
6. public void send();
7. }
8. -------------------------------------------------------------------------------------
9. public class MailSender implements Sender {
10.
11. @Override
12. public void send() {
13. System.out.println("this is mail sender!");
14. }
15. }
16. -------------------------------------------------------------------------------------
17. public class SmsSender implements Sender {
18.
19. @Override
20. public void send() {
21. System.out.println("this is sms sender!");
22. }
23. }
24. -------------------------------------------------------------------------------------
25. public class SendSmsFactory implements Provider {
26.
27. @Override
28. public Sender produce() {
29. return new SmsSender();
30. }
31. }
1. public class SendMailFactory implements Provider {
2.
3. @Override
4. public Sender produce() {
5. return new MailSender();
6. }
7. }
8. -------------------------------------------------------------------------------------
9. public class Test {
10. public static void main(String[] args) {
11. Provider provider = new SendMailFactory();
12. Sender sender = provider.produce();
13. sender.send();
14. }
15. }

3、建造者模式

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,
所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的 Test 结合起来得到的
1. public class Builder {
2. private List<Sender> list = new ArrayList<Sender>();
3.
4. public void produceMailSender(int count) {
5. for (int i = 0; i < count; i++) {
6. list.add(new MailSender());
7. }
8. }
9.
10. public void produceSmsSender(int count) {
11. for (int i = 0; i < count; i++) {
12. list.add(new SmsSender());
13. }
14. }
15. }
1. public class TestBuilder {
2. public static void main(String[] args) {
3. Builder builder = new Builder();
4. builder.produceMailSender(10);
5. }
6. }

4、适配器设计模式

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。
	主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式

1、类的适配器模式

1. public class Source {
2. public void method1() {
3. System.out.println("this is original method!");
4. }
5. }
6. -------------------------------------------------------------
7. public interface Targetable {
8. /* 与原类中的方法相同 */
9. public void method1();
10. /* 新类的方法 */
11. public void method2();
12. }
13. public class Adapter extends Source implements Targetable {
14. @Override
15. public void method2() {
16. System.out.println("this is the targetable method!");
17. }
18. }
19. public class AdapterTest {
20. public static void main(String[] args) {
21. Targetable target = new Adapter();
22. target.method1();
23. target.method2();
24. }
25. }

2、对象的适配器模式

1. public class Wrapper implements Targetable {
2. private Source source;
3.
4. public Wrapper(Source source) {
5. super();
6. this.source = source;
7. }
8.
9. @Override
10. public void method2() {
11. System.out.println("this is the targetable method!");
12. }
13.
14. @Override
15. public void method1() {
16. source.method1();
17. }
18. }
19. --------------------------------------------------------------
20. public class AdapterTest {
21.
22. public static void main(String[] args) {
23. Source source = new Source();
24. Targetable target = new Wrapper(source);
25. target.method1();
26. target.method2();
27. }
28. }

3、接口的适配器模式

接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接
口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个
问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原
始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

5、装饰模式(Decorator)

顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
1. public interface Sourceable {
2. public void method();
3. }
4. ----------------------------------------------------
5. public class Source implements Sourceable {
6. @Override
7. public void method() {
8. System.out.println("the original method!");
9. }
10. }
11. ----------------------------------------------------
12. public class Decorator implements Sourceable {
13. private Sourceable source;
14. public Decorator(Sourceable source) {
15. super();
16. this.source = source;
17. }
18.
19. @Override
20. public void method() {
21. System.out.println("before decorator!");
22. source.method();
23. System.out.println("after decorator!");
24. }
25. }
26. ----------------------------------------------------
27. public class DecoratorTest {
28. public static void main(String[] args) {
29. Sourceable source = new Source();
30. Sourceable obj = new Decorator(source);
31. obj.method();
32. }
33. }

7、策略模式

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法
的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,
属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种
算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可

8、观察者模式

观察者模式很好理解,类似于邮件订阅和 RSS 订阅,当我们浏览一些博客或 wiki 时,经常会看到 RSS 图标,就
这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其
它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

9、代理模式

谈谈 JVM 的内存结构和内存分配

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值