1、线程本质就是栈结构
2、进程从规模上要大于线程,进程是包含线程的,主方法就是主线程
3、线程在创立以后,各自之间的执行是互不干扰的
4、创建线程之后,如果没有start(),线程是无法启动的,但是线程调用start()只是让线程进入就绪状态,需要等待一小段时间才可以被分配处理器
5、分时操作系统:单核cup在一个时间段内就只能执行一个操作,在执行之前要获得时间片
6、操作系统在切换执行线程的过程中是有时间开销的,线程切换消耗将近达到1ms。线程的切换也叫做上下文切换
7、主线程属于当前执行的线程,但是并不是主线程一定先执行完。他只是先执行完的概率大。就算主线程执行完了,子线程还可以继续执行。主线程和子线程互不依赖。
8、多线程不一定比单线程快!多线程有额外的线程开销
9、CPU浪费,单线程传递10000个数据,每个需要5.01ms,总共50100ms其他时间等待,其他地区在打空转。而多线程的,就是发多个写数据命令,增加写时间,当去存取数据的时候,CPU可以去做其他事情。
10、(面试点)多线程使用场景:数据库传输,网络爬虫
11、多核处理机去处理单线程的时候,只有一个核在使用,其他核是浪费的
12、(面试点)线程的性能测试工具:Lmbench3
13、(面试点)减少上下文切换:
1、无锁并发编程:多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,采用一些方法来避免使用锁,如将数据的ID按照Hash算法来取模分段,不同的线程处理不同的段
2、CAS算法:Java的Atomic包使用CAS算法更新数据,所以不需要加锁
3、使用最少线程:避免创建不需要的线程,比如任务很少,但是创建很多线程,这样会导致大量线程处于等待状态。
4、协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换
14、Linux的一些指令
dump 快照,这是一种行为 快速打印日志
sudo -u 管理员命令执行
统计指令 uniq -c
筛选指令 grep
15、synchronized()加锁,只对引用类型加锁,不会对基本类型加锁
16、(面试点)如何造成死锁:
在上述的代码中,线程t1对A进行加锁,线程t2对B进行加锁,但是t1想要B,t2想要A,这样就会导致,两个线程都在等待对方释放锁,从而造成死锁。
17、(面试点)如何避免死锁:
1、避免一个线程同时获取多个锁。
2、避免一次线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
3、尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制。
4、对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况
18、synchronize是读写锁保证读和写都是准确的,不能用来修饰属性,能够用来修饰方法
volatile是读锁只能保证读是准确的,只能修饰属性
19、
内存屏障:
就是个约定,对他加标记,有这个标记就不能访问,少了这个标记就可以访问
缓冲行:
高速缓存的基本单位
原子操作 :
atomic 类,要么都成功,要么都失败,10个步骤,如果前九个执行成功最后一个失败了,前九个都要还原回去
缓存行填充:
意思是,尽可能在填充的时候把缓存行填满。读的频率比较高的适合填满缓存,这样读的速度就会快很多,因为整个缓存行就只有你一个数据,当你在读的时候就不会被缓存行的其他数据所堵塞
缓存命中:
在读取数据时,首先去高速缓存中搜索一下数据是否存在于高速缓存中,存在就直接从高速缓存中读取,减少读取时间
写名中:
不需要每次都去写回到内存中,先写在缓存行在写进内存,减少了对总线的占用,可以使其他的硬件去使用总线提高效率
写缺失(刷新缺失)(和写命中并不对应)
当从缓存行往内存中写i的时候,找不到i,其他线程,把i标记为无效数据了