目录
1. 进程和线程
进程:进程是资源分配的最小单位(CPU、内存、磁盘IO)
线程:线程是CPU调度的最小单位
单个进程中可以包含多个线程,线程必须依赖进程存在。多个线程间共享进程的资源,因此也会产生线程安全问题。
2. CPU核心和线程的关系
正常情况下,线程数和核心数是1:1的关系,像我们常见的双核CPU、四核CPU,但是Intel引入了超线程技术后,可以实现核心数和线程数1:2的关系。
多线程可以让多个线程同步执行共享处理器的资源,提高了计算机的性能。
3. CPU时间片轮转机制
上面说到,核心和线程数最多1:2,那么即使我们的计算机是16核,那也最多只能跑32个线程,事实真的是这样吗?其实不然,平常我们计算机可以启动的线程可以远远大于理论线程数,原因是因为操作系统为我们提供了一直CPU时间片轮转机制(RR调度)。
轮转机制的原理是每个进程都被分配了一个时间片段,即他允许运行的时间,当他的运行时间到了后,不管有没有运行完,都会把CPU资源让给下一个进程,而进程中又包含了线程,因此实际计算机可以启动的线程可以远远超过核心数的2倍。也就是说,我可以启动大量的线程,只要同一时间不全部运行,就可以了。这样大大提高了CPU的并发性和资源利用率。
另外,时间片执行完后,把资源给下一个进程,是需要时间的,需要更新表格、队列等操作(这个过程称之为上下文切换),因此就会存在一个问题,如果时间片设置的太短,则上下文切换太频繁,资源都浪费在切换上了,利用率太低。如果时间片设置太长,每个进程等待时间又太长,相应时间长。因此时间片不能设置的太长和太短,通常把时间片设置为100ms是一个比较合理的折中。
4. 并行和并发
并行:同一时刻,可以同时执行的任务数
并发:某个单位时间段内,可以执行的任务数
举个例子:并行就像,一个4车道的高速路,同时可以并排开4辆车,那么他的并发就是4.而一个小时内,这个高速路可以通行2000辆车,我们就说他的并发是2000/小时。
5. 高并发的意义、好处和注意事项:
充分利用CPU资源
加快用户响应时间
6. 线程
6.1 启动线程的方式 两种
- extends Thread然后start
- implements Runnable,然后交给Thread运行
区别:
Thread 是Java中对线程的唯一抽象
Runnable只是对任务的抽象(业务逻辑)Thread可以接受任意一个Runnable的实例并执行
6. 2中止
- stop()不推荐使用吗,因为它会强制终结一个线程,不能保证线程资源的正常释放
- interrupt()好比对线程打了一个招呼,线程不一定会立即停止自己的工作,也可能丝毫不理会,他会把线程的标志位设置为true
- Thread.interrupted() 可以判断线程是否已中断,他在判断的同时会把标志位重置为false
- isInterrputed() 判断线程是否已中断
6.3 run()和start()
start()重复调用会抛异常,new一个thread实例时,线程还没有和操作系统挂钩起来,只有start()后,才是真正意义上的启动了线程。start是让线程进入就绪队列,等待分配CPU,分到CPU后才开始执行run()方法。
run()方法是业务逻辑的具体实现的地方,可以重复执行,可以单独调用,本质就是一个类的成员方法。
6.4 yield()
使当前线程让出CPU占有权
6.5 join()面试常考
可以使线程顺序执行,比如b线程调用了a线程的join(),则a执行完后,b才会继续执行。如果b执行时,c调用了b线程的join(),则执行顺序就成了c--->b--->a
7. 线程间的共享
前面提到过,进程中的资源是被线程共享的,因此多个线程访问同一个对象或对象的成员变量,将会产生线程同步问题。为了解决这个问题,我们可以用过synchronized修饰方法或以同步块的方式使用,保证了线程对变量访问的可见性和排他性,他属于内置锁。
对象锁是锁当前类的实例,类锁是锁的当前类对应的class对象,实例可以有多个,class对象只有一个,因此类锁和对象锁是互不干扰的,也就是可以同步执行
静态方法加锁,就是类锁