java12-操作系统

165 阅读5分钟

1、操作系统中的进程及线程

操作系统的核心内容:进程管理、存储管理、文件系统管理、I/O设备管理

进程运行的实体,包括运行程序中占据的系统的资源,CPU、内存、网络资源,同一时刻运行两次程序是两个进程

一个进程具有多个线程,共享进程资源

进程的管理,主要分为两个方面:进程通信和进程调度。

  • 进程通信的例子是进程A产生数据,进程B打印数据,故A,B需要通信。
  • 进程调度:只有一个CPU可用,CPU需对下一个进程或线程选择

一个进程的组成:进程控制块,数据段,程序段。进程的控制,通过原语进行控制

2、线程池

线程池的概念: JVM在HotSpot线程模型下,JVM线程会一对一的映射为内核线程,导致的问题,创建以及回收都会去内核去创建即回收,导致时间和资源会花费的多

线程池的目的: 提高线程的高可用性以及固定线程的数量,避免重复的创建

创建:使用ThreadPoolEXecutor,不使用Executors,因为更能了解线程池运行的规则,避免资源耗尽的风险

主要是构造线程池提供了几个参数:
CorePoolSize  核心线程的数量  一般的设置最大的线程数量和核心线程数量相等
maximumPoolSize 最大的线程的数量
keepAliveTime线程空余的时间  是指当前的线程数大于核心的线程数了,到到时间进行回收
workQueue 阻塞的队列
Handler 任务的拒绝策略

任务提交的过程:

  • 在提交的时候,首先判断核心线程的数量是否小于CorePoolSIze,如果小于,则直接创建新的线程执行任务 (因为任务是放在线程中进行执行的)
  • 如果大于corepoolSize,判断wokeQueue阻塞队列是否已经满了,如果没有满加入阻塞队列
  • 如果阻塞队列满了,查看是否大于maixmumSize,如果没有,直接创建一个线程
  • 如果大于最大的线程数量,则执行拒绝策略,具体的是自己实现的handler

如何设置线程池中核心线程的数量?

  • 查看自己的任务是CPU密集的还是IO密集的,根据情况进行设定线程池中核心线程的数量

如何进行设置数量?

  • 在创建线程池的时候,ThreadPoolExcutors线程的实现类中,重点维护一个Ctl参数,用高三为表示线程池的状态,低29为表示线程的数量

3、ThreadLocal (多线程的情况下,实现了数据的隔离)

首先,ThreadLocal是一个类,一般我们定义一个成员的变量,可以实现多个线程进行操作和修改,变量对于多个线程之间并不独立,是共享的变量,使用ThreadLocal将变量进行私有化

ThreadLocal的作用主要是做数据隔离填充的数据只属于当前线程,变量的数据对别的线程而言是相对隔离的,在多线程环境下,如何防止自己的变量被其它线程篡改。

实际的使用场景:Spring采用Threadlocal的方式来保证单个线程中的数据库操作使用的是同一个数据库连接,同时,采用这种方式可以使业务层使用事务时不需要感知并管理connection对象,通过传播级别,巧妙地管理多个事务配置之间的切换,挂起和恢复。使用map进行存储,key是datasource,value是connection,保证了单个线程只能操作自己的数据源

详细的解释:

ThreadLocal   t1  t2
             
local1       threadlocals

local2

同一个变量local,在线程t1是小红,在线程t2中是小兰,别人额印象才是最重要的

总共包含三个方法:set get remove
怎么去set一个值呢?

ThreadLocal<String> localName = new ThreadLocal();
localName.set("张三");
String name = localName.get();
localName.remove();

主要的原理: Set的时候,是通过ThreadLocalMap实现的,而当创建一个线程的时候,ThreadLocalMap是通过线程的一个变量threadlocals来获得的

public void set(T value) {
    Thread t = Thread.currentThread();// 获取当前线程 
    ThreadLocalMap map = getMap(t);// 获取ThreadLocalMap对象 
    if (map != null) // 校验对象是否为空 
         map.set(this, value); // 不为空set
     else createMap(t, value); // 为空创建一个map对象 }
```js
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

其中对应的ThreadLocalMap中的key是一个弱引用(肉体已经走了,但是还会想念),只要发生垃圾回收的时候,弱引用指向的对象就会被回收。此时的对应的key就已经回收。则会发生内存泄漏,因为此时的value还一直在。所以一般get\set完的时候使用remove方式进行回收value的值

但对于在线程中使用ThreadLocal这个类时候需要注意,一般的线程是重复利用的,如果不执行remove方法,则容易发生内存泄漏

内存泄漏的条件?

  • 弱引用被回收,但值一致还在
  • 不执行set、get、remove方法
  • 线程池中的线程一直不销毁

4、OS线程之间的通信

常见的进程同步与互斥的机制:信号量与PV操作 管程

  • PV操作可以实现进程之间的互斥和同步
  • 管程只能实现进程的互斥,对PV操作进行了封装

对于java中也采用管程机制,Synchronized,一次只允许一个线程进行调用

Linux常见的进程通信(IPC interProcess Commucation)

  • 管道:将前一个命令的输出作为后一个命令的输入,只能单向流动,半双工通信的方式,借助内核缓冲区实现,不可反复读取,只能传输无格式的字节流
  • 消息队列:管道的传输方式不适合频繁的交换数据,放在消息队列中自己去访问,消息队列在内核中 缺点:需要内核的接入
  • 共享内存:通过系统调用剪一块共享的内存,但有点浪费空间
  • 信号量与PV操作:传递的是进程的状态
  • socket套接字:跨网络之间的通信,也能完成同主机之间的通信