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.