JAVA多线程

1.volatile

volatile变量可以保证 可见性【 当有线程在主存 写值的时候,其他线程要等待最新值写完才能读取。 但是当线程读完值之后,就不会去管其他线程是否重新写入最新值到主存了】

volatile相对于synchronized ,具有可见性,禁止指令重排序,却不具有 原子性。

如果不加volatile, 线程就不会每次都到主内存中去取值,也不会改完值后立即写入主内存。 用了volatile ,就能保证,每次更改后都立即写入主内存,而且读数据也是去主内存里读取。

为什么volatile 不能保证线程一定安全,因为 volatile修饰的变量,只是保证从主内存加载到线程工作内存的值是最新的。比如:

如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值。

在线程1对 count进行修改之后,会write到主内存中,主内存中的count变量就会变为6。

线程2由于已经进行read,load操作,即使线程1更改了count值,线程2也不会再去主存读取最新值了,在进行运算之后,也会更新主内存count的变量值为6。

2.Vector是一个线程安全类吗?

Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的

HashTable 是线程安全的,但是 HashMap不是线程安全的。

延申 :

ArrayList 对应的线程安全的list【应对高并发】 :CopyOnWriteArrayList (写需要加锁,读不需加锁)【完美解决了并发的问题】

hashMap 对应的线程安全的Map【应对高并发】: ConcurrentHashMap

3.Java中notify 和 notifyAll有什么区别?

notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁 , 确保了至少有一个线程能继续运行。

4.为什么wait, notify 和 notifyAll这些方法不在thread类里面?

因为JAVA提供的锁 是对象级的 而不是 线程级的。每个对象都有锁,通过Object里的方法,才能操作各自的锁。

5.为什么wait和notify方法要在同步块中调用?

主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。

6.Java中堆和栈有什么不同?

对于一个CPU,线程数总是大于或等于 核心数的。

每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,线程 为了提升效率线程会从堆中弄一个缓存到自己 的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了。

volatile关键字的两层语义

1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(如果是写操作,它会导致其他线程 中对应的缓存行无效,需要再次向主存获取。)

2)禁止进行指令重排序(一定程度保证 有序性)。


7. JVM中哪个参数是用来控制线程的堆栈大小的

 -Xss参数用来控制线程的堆栈大小

8.有三个线程T1,T2,T3,怎么确保它们按顺序执行?

可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。(T3调用T2,T2调用T1)

9.Thread类中的yield方法有什么作用?

它是一个静态方法,Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。

10.如果你提交任务时,线程池队列已满。会时发会生什么?

会抛出一个RejectedExecutionException异常

11.什么是阻塞式方法?

阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket的accept()方法就是一直等待客户端连接

12.Java中的ReadWriteLock是什么?

读写锁是用来提升并发程序性能的。一个用于只读操作一个用于写。在没有写线程的情况下,一个读锁可能会同时被多个读线程 持有,写锁 只能被一个写线程 独占。

13.多线程中调用wait() 和 sleep()方法有什么不同?

wait()方法用于线程间通信,它会释放锁。而 sleep()方法仅仅让当前线程停止执行一段时间,但不会释放锁。

14.什么是线程的调度

线程调度是指按照特定机制为多个线程分配CPU的使用权。

15.为什么通过Excutor 来启动线程(创建线程池) 比用Thread的 start()好?

Executors.newCachedThreadPool(); //创建缓存线程池

重用存在的线程,减少线程创建、销毁的开销,性能佳

线程池可以 统一管理,定时执行、定期执行、并发数控制等功能

ScheduledThreadPool 里边传参进行控制

16.什么是线程?

线程是操作系统进行运算调度的最小单位。它被包含在进程中,是进程中实际运作的单位。

17.什么是线程安全和线程不安全?

加锁的就是安全的,不加锁就是不安全。

18.Synchronized 详解:

要求加锁前,要清空工作内存中的值,从主内存中重新拿取。

要求解锁时,要把工作内存中计算的值,刷新到主内存。

对象锁:【N个线程N把锁 ,并行运行】

加上的synchronized 是  对象锁  的话,不同对象是两把不同的锁,不会相互影响的。各自在各自的锁里执行。 两个线程会交替的执行着,但是不会互相影响的。

synchronized(this)【同步代码块】(n个线程时,拥有n把对象锁,同时执行;this表示各个线程对象本身做为一把锁)

synchronized method 【同步方法】(n个线程时,只有一把锁)

【由几个不同的实例对象创建出来的锁,是几个不同的锁,线程并行运行】

类锁:  【N个线程 一 把锁 ,串行运行】

synchronized加在 静态(static)方法上: (因为 是静态的方法,所以锁只有一把)

synchronized(类.class):因为类只有一个,所以n个线程也只能去持有一把锁,线程之间先拿到这把锁的线程先运行,其他线程等待,也就是串行运行的。

面试问题 :

①.> 如果 同一个类里 ,有两个方法加了 修饰符 synchronized ,那么这两个方法就无法同时运行,因为他们在同一个类中,用的同一把锁。

②.> 在synchronized 中抛出异常的话,会释放锁。

③> synchronized 使用注意点 :

1)锁对象不能为空;

2)作用域不能太大(多线程就是为了并行运行,提高效率,作用域太大不就变成串行运行了);

3)避免死锁;

④> 如何选择 Lock 与 synchronized :优先使用 synchronized 除非是需要灵活加解锁 ,采用 Lock

⑤> Synchronized 与 lock 与 volatile 的区别

Synchronized :不可中断锁,适合竞争不激烈  【保证线程原子性,可见性】

Lock :可中断(通过unlock即可),适合竞争激烈。【保证 原子性】

volatile : 【可保证 线程 可见性, 一定程度的有序性】

19.AQS (同步器)

CountDownLatch : await()挂起, countDown() 计数器-1 ,为0时唤醒线程。

Semaphore (信号量)[控制并发量]:

对test(threadNum) 进行并发限制,new Semaphore(20):同一时刻,允许20个线程 并发执行 。

在 semaphore.acquire() 和 semaphore.release()之间的代码,同一时刻只允许指定个数的线程进入

eg :要求 并发数是3 ,超过的请求就放弃掉:

tryAcquire() :尝试获取一个许可 (3个并发),获取完后,就释放掉。

cyclicBarrer :new cyclicBarrer(4) 一次有4个线程await()后,才会将这4个线程一起并发执行。

ReentrantLock :【可重入锁】

ReentrantLock 与 Synchronized 的区别:【但是优先选择synchronized,因为性能差不多,操作简单,而且也不需要老是要记得释放锁】

ReentrantLock 的优势:

可以设置公平锁 与 非公平锁。Synchronized  只有非公平的锁。

提供了Condition类。可以分组唤醒需要唤醒的线程。 Synchronized  只能 随机唤醒一个线程,或者唤醒所有的线程。

提供能够中断等待锁的机制。

ReentrantReadWriteLock:

在没有任何读锁的情况下,才能获取写锁。

适合读操作很多,写操作很少的情况下使用用该锁。因为如果写的操作多,很可能因为一直没有抢到锁,而一直不能进行写操作。

20.形成死锁的四个条件:

1)互斥条件

2)请求保持

3)不可剥夺

4)环路等待

21.扩容

垂直扩容:增大内存 【提升成员的性能】

水平扩容:增加服务器(集群) 【增加成员】

使用缓存来提高并发处理的能力

22.线程的流程图

(双击放大看)

五个状态:NEW,  就绪Ready,Running ,Blocked,Waitting,Timed_waitting,Terminated

1)。new Thread()线程 ,进入NEW 状态

2)。start(),线程进入 就绪Ready状态

3)。线程获取到CPU使用权限,进入Running状态。

4)。线程调用 yield() 方法,释放 CPU,让其他线程可以获取CPU使用权限。自己进入 就绪Ready 状态。

5)。synchronized(上图显示的也是属于进入阻塞状态),会使线程都会进入阻塞状态,

6)。sleep(),join()结束,线程进入 Waitting 状态 ,nofiry(),notifyAll() 线程会进入就绪Ready状态。

7).sleep(long),join(long),带参数的这种,就会进入 Timed_waitting 状态。

Object 的方法 : wait() 进入等待阻塞中, notify(),notifyAll()唤醒

Thread的方法 : start(),run(),  sleep(),yield(),join()

23.为什么我们调用 start() 方法时会 执行 run()方法,为什么我们不能直接调用run()?

因为 start(),是启动一个新的线程,进入就绪状态,有了CPU使用权后,才会run起来。如果直接调用 run(),则没有启动新线程,而是在主线程main()直接执行。

25.三次握手

客户端–发送带有 SYN 标志的数据包–一次握手–服务端

服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端

客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端

26.TCP协议,UDP协议的 区别

UDP在传输数据的时候b不需要建立连接,主机在收到UDPb报文后,不需要给出任何确认。一般用户 即时通信 ,eg: QQ语音,视频等。

TCP在传输数据之前必须先建立连接,数据传输完后要释放连接。一般用于 文件传输,发送和接收邮件。

27.HTTP长连接 : 使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。

28.访问 一个URL 的过程:

DNS解析

TCP连接

发送HTTP请求

服务器处理请求并返回HTTP报文

浏览器解析渲染页面

连接结束

29.

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容