部分图片源于三分恶 ,作者老三
并行和并发的区别
并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。
并发不是并行。并行是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了。
在很多情况下,并发的效果比并行好,因为操作系统和硬件的总资源一般很少,但能支持系统同时做很多事情。
借用github上的一幅图理解
并发就是多个队(多个任务)等待一个咖啡机(处理器/CPU),并行就是多个队有多个咖啡机
什么是多线程中的上下文切换
通过上图可以看到,多线程的时候,多个任务要处理,CPU资源是有限的,每个队看作一个任务,每个人看作一个时间片,当一个时间片到了当前的任务就要让出CPU,去处理别的任务。
多线程会共同使用一组计算机上的CPU,而线程数大于给程序分配的CPU数量时,为了让各个线程都有执行的机会,就需要轮转使用CPU。不同的线程切换使用CPU发生的切换数据等就是上下文切换。
守护线程和本地线程
java中的线程分为两种:守护线程(Daemon)和用户线程(User)。任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(boolon);true则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。两者的区别:唯一的区别是判断虚拟机(JVM)何时离开,Daemon是为其他线程提供服务,如果全部的UserThread已经撤离,Daemon没有可服务的线程,JVM撤离。也可以理解为守护线程是JVM自动创建的线程(但不一定),用户线程是程序创建的线程;比如JVM的垃圾回收线程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。
扩展:ThreadDump打印出来的线程信息,含有daemon字样的线程即为守护进程,可能会有:服务守护进程、编译守护进程、windows下的监听Ctrl+break的守护进程、Finalizer守护进程、引用处理守护进程、GC守护进程。
线程的状态及线程间的通信
当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法可以用于线程间通信关于资源的锁的状态。
ThreadLocal存储上下文信息
在控制层拦截请求把用户信息存入ThreadLocal,这样我们在任何一个地方,都可以取出ThreadLocal中存的用户数据。
使用
/**
* 当前用户线程
*/
public class AuthThreadLocal {
private AuthThreadLocal() {
}
private static final ThreadLocal<AuthTokenData> LOCAL = new ThreadLocal<>();
/**
* 将user放到threadLocal
*
* @param auth user
*/
public static void setAuth(AuthTokenData auth) {
LOCAL.set(auth);
}
/**
* 返回当前线程中的user对象
*
* @return 返回当前线程中的user对象
*/
public static AuthTokenData getAuth() {
return LOCAL.get();
}
/**
* 删除当前线程中的user对象
*/
public static void remove() {
LOCAL.remove();
}
}
悲观锁和乐观锁
https://developer.aliyun.com/article/852353?spm=a2c6h.26396819.0.0.7a5f3e18mUYSxz
Java线程池
https://developer.aliyun.com/article/852287?spm=a2c6h.26396819.0.0.7a5f3e18MrrERE
volatile实现原理
https://developer.aliyun.com/article/848755?spm=a2c6h.26396819.0.0.7a5f3e18an2G1Y
Semaphore(信号量)
Java中的Semaphore是一种新的同步类,它是一个计数信号。从概念上讲,从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。
public class SemaphoreTest {
private static final int THREAD_COUNT = 30;
private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire();
System.out.println("save data");
s.release();
} catch (InterruptedException e) {
}
}
});
}
threadPool.shutdown();
}
}