CComma's Blog

CComma's Blog

Connect the world

问题

在编码过程中,经常会遇到用某个数值来表示某种状态、类型或者阶段的情况,比如有这样一个枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public enum ComputerState {

OPEN(10), //开启

CLOSE(11), //关闭

OFF_LINE(12), //离线

FAULT(200), //故障

UNKNOWN(255); //未知

private int code;
ComputerState(int code) { this.code = code; }

}

通常我们希望将表示状态的数值存入数据库,即 ComputerState.OPEN 存入数据库取值为 10。

Read more »

1. I/O 硬件原理

1.1. I/O 设备

I/O 设备大致可以分为 块设备(block device)字符设备(character device)

块设备:
块设备把信息存储在固定大小的块中,大小在 512 字节至 65536 字节之间。所有传输以一个或多个完整的连续块为单位。
块设备的基本特征是每个块都能独立于其他块而读写。硬盘、蓝光光盘和 USB 是最常见的块设备。

字符设备:
字符设备以字符为单位发送或接收一个字符流,而不考虑任何块结构。
字符设备是不可寻址的,也没有任何寻道操作。打印机、网络接口、鼠标 (用作指点设备)、以及大多数与磁盘不同的设备都可看作字符设备。

Read more »

1. 无存储器抽象

早期大型计算机(20 世纪 60 年代之前)、小型计算机(20 世纪 70 年代之前)和个人计算机(20 世纪 80 年代之前)都没有存储器抽象。每一个程序都直接访问物理内存。

例如:MOV REGISTER1, 1000,计算机会将位置为 1000 的物理内存中的内容移到 REGISTER1 中。

Read more »

深入理解计算机系统使用指南:https://book.douban.com/review/5627139/

1. 什么是操作系统

操作系统所处的位置
image.png
硬件包括芯片、电路板、磁盘、键盘、显示器以及类似的设备。

用户态和内核态
多数计算机有两种运行模式:内核态和用户态。

  • 内核态:操作系统运行在内核态,具有对所有硬件的完全访问权,可以执行机器能够运行的任何指令。内核态的程序由硬件保护,防止用户试图对其进行修改。
  • 用户态:软件的其余部分运行在用户态,只使用了机器指令中的一个子集。
Read more »

把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题

Read more »

1. CountDownLatch

概述: 用来控制一个线程等待多个线程

原理:
维护了一个计数器 cnt,每次调用 countDown () 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await () 方法而在等待的线程就会被唤醒

API:

  • await(): 调用 await () 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行
  • await(long timeout, TimeUnit unit): 和 await () 类似,只不过等待一定的时间后 count 值还没变为 0 的话就会继续执行
  • countDown(): 将 count 值减 1

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CountdownLatchExample {

public static void main(String[] args) throws InterruptedException {
final int totalThread = 10;
CountDownLatch countDownLatch = new CountDownLatch(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < totalThread; i++) {
executorService.execute(() -> {
System.out.print("run..");
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println("end");
executorService.shutdown();
}
}
// run..run..run..run..run..run..run..run..run..run..end

场景:

  1. 启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行
  2. 实现多个线程开始执行任务的最大并行性
    CountDownLatch (1),多个线程挂起,当主线程调用 countDown () 时,多个线程同时被唤醒

不足:
CountDownLatch 是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当 CountDownLatch 使用完毕后,它不能再次被使用。

Read more »

深入解析 volatile 、CAS 的实现原理

处理器如何实现原子操作
处理器提供 总线锁定缓存锁 定两个机制来保证复杂内存操作的原子性。

  • 使用总线锁保证原子性
    所谓总线锁就是使用处理器提供的一个 LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占使用共享内存。
  • 使用缓存锁保证原子性

底层用 lock 实现,如果是多核添加 lock 指令。
lock 用于在多处理器中执行指令时对共享内存的独占使用。
它的作用是能够将当前处理器对应缓存的内容刷新到内存,并使其他处理器对应的缓存失效
另外还提供了有序的指令无法越过这个内存屏障的作用

volatile 变量自身具有以下特性:

  • [可见性](#1. 保证线程可见性):对一个 volatile 变量的读,总是能看到 (任意线程) 对这个 volatile 变量最后的写入。
  • [禁止指令重排](#2. 禁止指令重排)

想了解以上特性的原理,需先了解 [处理器缓存](#0. 处理器缓存)。

Read more »

面试必备之深入理解自旋锁

自旋锁的思想是让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以避免进入阻塞状态

缺点: 如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗 CPU。使用不当会造成 CPU 使用率极高。

Read more »

  • boolean tryLock(long time, TimeUnit unit): 尝试获得锁,阻塞时间不会超过给定的值;如果成功返回 true
  • void  lockInterruptibly(): 获得锁,但是会不确定地发生阻塞。如果线程被中断,抛出一个 InterruptedException 异常
Read more »
0%