本次整理的内容如下:
进程是一个可执行的程序,是系统资源分配的基本单位;线程是进程内相对独立的可执行单元,是操作系统进行任务调度的基本单位。
由于每个进程都有独立的内存空间,进程之间的数据交换需要通过操作系统内核。需要在操作系统内核中开辟一块缓冲区,进程A将需要将数据拷贝到缓冲区中,进程B从缓冲区冲读取数据。因为共享内存没有互斥访问的功能,需配合信号量进行互斥访问。
管道的特点:
只允许具有血缘关系的进程间通讯,只允许单向通讯,进程在管道在,进程消失管道消失。管道内部通过环形队列实现。
有名管道(命名管道):
通过文件的方式实现进程间的通信。允许无血缘关系的进程间的通信
由消息组成的链表,存在系统内核中。克服了信号量传递的信息少,管道只能承载无格式的字符流及缓冲区的大小受限等特点。通过消息类型区分消息。
本质是一个计数器,不以传送数据为目的,主要用来保护共享资源,使得资源在一个时刻只有一个进程独享。
可用于不同机器间进程的通信。
套接字包括3个属性:域、类型、 协议。
创建socket 通过bind命名绑定端口,listen创建队列保存未处理的客户端请求,accept等待客户端的连接,connect服务端连接客户端socket,close关闭服务端客户端的连接。
stream和datagram的区别:
stream能提供有序的、可靠的、双向的、基于连接的字节流(TCP),会有拆包粘包问题。
datagram是无连接、不可靠、使用固定大小的缓冲区的数据报服务(UDP),因为基于数据报,且有固定的大小,所以不会有拆包粘包问题。
详细请参考: 进程间的五种通信方式介绍
共享内存:
Java采用的就是共享内存,内存共享方式必须通过锁或者CAS技术来获取或者修改共享的变量,看起来比较简单,但是锁的使用难度比较大,业务复杂的话还有可能发生死锁。
消息传递:
Actor模型即是一个异步的、非阻塞的消息传递机制。Akka是对于Java的Actor模型库,用于构建高并发、分布式、可容错、事件驱动的基于JVM的应用。消息传递方式就是显示的通过发送消息来进行线程间通信,对于大型复杂的系统,可能优势更足。
详细请参考: Java内存模型分析
优点:
充分利用cpu的资源,提高cpu的使用率,使程序的运行效率提高。
缺点:
有大量的线程会影响性能,操作系统会在线程之间切换,会增加内存的开销。可能会产生死锁、存在线程之间的并发问题。
详细请参考: 并发编程挑战:死锁与上下文切换
静态分配,每个线程开始前就获取需要的所有资源。
动态分配,每个线程请求获取资源时本身不占有资源。
当一个线程不能获取所有的资源时,进入等待状态,其已经获取的资源被隐式释放,重新加入到系统的资源列表中,可被其他线程使用。
详细请参考: 【SharingObjects】ThreadLocal
java.lang.ThreadLocal.ThreadLocalMap.Entry:
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { //重点!!!!! super(k); value = v; } }
因为ThreadLocalMap中的key是弱引用,而key指向的对象是threadLocal,一旦把threadLocal实例置为null之后,没有任何强引用的对象指向threadLocal对象,因此threadLocal对象会被Gc回收,但与之关联的value却不能被回收,只有当前线程结束后,对应的map value才会被回收。如果当前线程没结束,可能会导致内存泄漏。
如线程池的场景,在线程中将threadlocal置为null,但线程没被销毁且一直不被使用,就可能会导致内存泄漏
在调用get、set、remove 方法时,会清除线程map中所有key为null 的value。所以在不使用threadLocal时调用remove移除对应的对象。
ThreadPoolExecutor继承关系图:
shutDown方法执行之后会变成SHUTDOWN状态,无法接受新任务,随后等待已提交的任务执行完成。
shutDownNow方法执行之后变成STOP状态,无法接受新任务。并对执行中的线程执行Thread.interrupt()方法。
当任务来时,如果当前的线程数到达核心线程数,会将任务加入阻塞队列中,如果阻塞队列满了之后,会继续创建线程直到线程数量达到最大线程数,如果线程数量已经达到最大线程数量,且任务队列满了之后,会执行拒绝策略。
如果想让核心线程被回收,可以使用allowCoreThreadTimeOut参数,如果为false(默认值),核心线程即使在空闲时也保持活动状态。如果true,核心线程使用keepAliveTime来超时等待工作。
java.util.concurrent.Executors类:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
生成一个固定大小的线程池,此时核心线程数和最大线程数相等,keepAliveTime = 0 ,任务队列采取 LinkedBlockingQueue 无界队列(也可设置为有界队列)。
适用于为了满足资源管理需求,而需要限制当前线程数量的应用场景,比如负载比较重的服务器。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
生成只有一个线程的线程池,核心线程数与最大线程数都是1,keepAliveTime = 0,任务队列采取LinkedBlockingQueue,适用于需要保证顺序地执行各个任务,并且在任意时间点不会有多个线程是活动的应用场景。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
核心线程数是0,最大线程数是int最大值,keepaliveTime 为60秒,任务队列采取SynchronousQueue,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。
public static ScheduledExecutorService newSingleThreadScheduledExecutor() { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1)); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
定长的线程池,支持周期性任务,最大线程数是int最大值,超时时间为0,任务队列使用DelayedWorkQueue,适用于需要多个后台执行周期任务,同时为了满足资源管理需求而需要限制后台线程的数量的应用场景。
任务执行失败后,只会影响到当前执行任务的线程,对于整个线程池是没有影响的。
详细请参考: ThreadPoolExecutor线程池任务执行失败的时候会怎样
kill -9 pid 操作系统内核级别强行杀死某个进程。
kill -15 pid 发送一个通知,告知应用主动关闭。
ApplicationContext接受到通知之后,会执行DisposableBean中的destroy方法。
一般我们在destroy方法中做一些善后逻辑。
调用shutdown方法,进行关闭。