1、工作线程是不是越多越好?
不是。a、服务器cpu核数有限,所以同时并发或者并行的线程数是有限的,所以1核cpu设置1000个线程是没有意义的。
b、线程切换也是有开销的。频繁切换线程会使性能降低。
2、调用sleep()函数的时候,县城是否会占用着CPU?
不占用,sleep()函数切换时会把cpu让出来。accept()阻塞和recv()阻塞时也会让出cpu。
3、cpu单核,做多线程有用吗?
多线程并发是有用的,但是起的线程数量太多会造成线程频繁上下文切换,开销大,性能衰弱。
4.线程池中多余的线程是如何回收的?
ThreadPoolExecutor回收工作线程,一条线程getTask()返回null,就会被回收。
分两种场景。
- 未调用shutdown() ,RUNNING状态下全部任务执行完成的场景
线程数量大于corePoolSize,线程超时阻塞,超时唤醒后CAS减少工作线程数,如果CAS成功,返回null,线程回收。否则进入下一次循环。当工作者线程数量小于等于corePoolSize,就可以一直阻塞了。
- 调用shutdown() ,全部任务执行完成的场景
shutdown() 会向所有线程发出中断信号,这时有两种可能。
2.1)所有线程都在阻塞
中断唤醒,进入循环,都符合第一个if判断条件,都返回null,所有线程回收。
2.2)任务还没有完全执行完
至少会有一条线程被回收。在processWorkerExit(Worker w, boolean completedAbruptly)方法里会调用tryTerminate(),向任意空闲线程发出中断信号。所有被阻塞的线程,最终都会被一个个唤醒,回收。
链接:https://mp.weixin.qq.com/s/y1R8iGyLmBUWh2-n4fyF_g
5.开启线程的三种方式
继承Thread类实现多线程/覆写Runnable()接口实现多线程/覆写Callable接口实现多线程
https://blog.csdn.net/weixin_41891854/article/details/81265772
6.线程和进程的区别
进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。线程是在进程中的,一个进程可以有多个线程,线程几乎不拥有独自资源,多个线程共享进程中的代码和数据
7.为什么要有线程,而不是仅仅用进程?
第一:因为进程拥有独立的堆栈空间和数据,当启动一个新的进程必须分配给它独立的地址空间,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭。
第二:体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。
第三:体现在CPU系统上面,线程使得CPU系统更加有效,因为操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
https://blog.csdn.net/linux12121/article/details/51786233
8.run()和start()方法区别
主要区别在于start()方法中会在新的线程中执行run()方法,且一个线程只能调用一次。start()方法,多次调用会有IllegalStateException。run()方法调用只是在本线程执行run()中代码,可多次调用。
9.如何控制某个方法允许并发访问线程的个数?
Semaphore 是一个计数信号量,必须由获取它的线程释放。
10.在Java中wait和seelp方法的不同
sleep()方法是Thread类的方法,因此它不能改变对象的机锁。
wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象
Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。
11.谈谈wait/notify关键字的理解
当一个线程执行到wait()方法时(线程休眠且释放机锁),它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程从锁池中获得机锁,然后回到wait()前的中断现现场。
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止
12.线程阻塞:
线程在运行的过程中因为某些原因而发生阻塞,阻塞状态的线程的特点是:该线程放弃CPU的使用,暂停运行,只有等到导致阻塞的原因消除之后才回复运行。或者是被其他的线程中断,该线程也会退出阻塞状态,同时抛出InterruptedException。
导致阻塞的原因有很多种,大致分为三种来讨论,分别是一般线程中的阻塞,Socket客户端的阻塞,Socket服务器端的阻塞。
一般线程中的阻塞:
A、线程执行了Thread.sleep(int millsecond);方法,当前线程放弃CPU,睡眠一段时间,然后再恢复执行
B、线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
C、线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
D、线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
Socket客户端的阻塞:
A、请求与服务器连接时,调用connect方法,进入阻塞状态,直至连接成功。
B、当从Socket输入流读取数据时,在读取足够的数据之前会进入阻塞状态。比如说通过BufferedReader类使用readLine()方法时,在没有读出一行数据之前,数据量就不算是足够,会处在阻塞状态下。
C、调用Socket的setSoLinger()方法关闭了Socket延迟,当执行Socket的close方法时,会进入阻塞状态,知道底层Socket发送完所有的剩余数据
Socket服务器的阻塞:
A、线程执行ServerSocket的accept()方法,等待客户的连接,知道接收到客户的连接,才从accept方法中返回一个Socket对象
B、从Socket输入流读取数据时,如果输入流没有足够的数据,就会进入阻塞状态
D、线程向Socket的输出流写入一批数据,可能进入阻塞状态
13.关闭线程的正确方法:
1.使用interrupt(),需要捕捉InterruptedException,
2.使用退出标志,使线程正常退出,也就是当 run() 方法完成后线程中止
注意:不使用stop(),会释放锁,线程不安全,原因是:
(1)stop方法是过时的从Java编码规则来说,已经过时的方法不建议采用。
(2)stop方法会导致代码逻辑不完整stop方法是一种“恶意”的中断,一旦执行stop方法,即终止当前正在运行的线程,不管线程逻辑是否完整,这是非常危险的。
(3)stop方法会破坏原子逻辑多线程为了解决共享资源抢占的问题,使用了锁概念,避免资源不同步,但是正因此原因,stop方法却会带来更大的麻烦:它会丢弃所有的锁,导致原子逻辑受损。