# Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

0%

## rust学习笔记-所有权一

发表于 2021-04-18 分类于 语言 ， Rust 阅读次数： Disqus：

最近在看 《rust 权威指南》，还是难度比较大的，它里面的一些概念跟之前的用过的都有比较大的差别
比起有 gc 的虚拟机语言，跟像 C 和 C++这种主动释放内存的，rust 有他的独特点，主要是有三条

- Rust中的每一个值都有一个对应的变量作为它的所有者。
- 在同一时间内，值有且只有一个所有者。
- 当所有者离开自己的作用域时，它持有的值就会被释放掉。
  这里有两个重点：
- s 在进入作用域后才变得有效
- 它会保持自己的有效性直到自己离开作用域为止

然后看个案例

    let x = 5;
    let y = x;

这个其实有两种，一般可以认为比较多实现的会使用 copy on write 之类的，先让两个都指向同一个快 5 的存储，在发生变更后开始正式拷贝，但是涉及到内存处理的便利性，对于这类简单类型，可以直接拷贝
但是对于非基础类型

    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}, world!", s1);

有可能认为有两种内存分布可能
先看下 string 的内存结构
第一种可能是
第二种是
我们来尝试编译下
发现有这个错误，其实在 rust 中`let y = x`这个行为的实质是移动，在赋值给 y 之后 x 就无效了
这样子就不会造成脱离作用域时，对同一块内存区域的二次释放，如果需要复制，可以使用 clone 方法

    let s1 = String::from("hello");
    let s2 = s1.clone();

    println!("s1 = {}, s2 = {}", s1, s2);

这里其实会有点疑惑，为什么前面的`x, y` 的行为跟 `s1, s2` 的不一样，其实主要是基本类型和 string 这类的不定大小的类型的内存分配方式不同，`x, y`这类整型可以直接确定大小，可以直接在栈上分配，而像 string 和其他的变体结构体，其大小都是不能在编译时确定，所以需要在堆上进行分配

## rust学习笔记-所有权二

发表于 2021-04-18 分类于 语言 ， Rust 阅读次数： Disqus：

这里需要说道函数和返回值了
可以看书上的这个例子
对于这种情况，当进入函数内部时，会把传入的变量的所有权转移进函数内部，如果最后还是要返回该变量，但是如果此时还要返回别的计算结果，就可能需要笨拙地使用元组

### 引用

此时我们就可以用引用来解决这个问题

    fn main() {
        let s1 = String::from("hello");
        let len = calculate_length(&s1);

        println!("The length of '{}' is {}", s1, len);
    }
    fn calculate_length(s: &String) -> usize {
        s.len()
    }

这里的&符号就是引用的语义，它们允许你在不获得所有权的前提下使用值
由于引用不持有值的所有权，所以当引用离开当前作用域时，它指向的值也不会被丢弃

### 可变引用

而当我们尝试对引用的字符串进行修改时

    fn main() {
        let s1 = String::from("hello");
        change(&s1);
    }
    fn change(s: &String) {
        s.push_str(", world");
    }

就会有以下报错，
其实也很容易发现，毕竟没有 mut 指出这是可变引用，同时需要将 s1 改成 mut 可变的

    fn main() {
        let mut s1 = String::from("hello");
        change(&mut s1);
    }

    fn change(s: &mut String) {
        s.push_str(", world");
    }

再看一个例子

    fn main() {
        let mut s1 = String::from("hello");
        let r1 = &mut s1;
        let r2 = &mut s1;
    }

这个例子在书里是会报错的，因为同时存在一个以上的可变引用，但是在我运行的版本里前面这段没有报错，只有当我真的要去更改的时候

    fn main() {
        let mut s1 = String::from("hello");
        let mut r1 = &mut s1;
        let mut r2 = &mut s1;
        change(&mut r1);
        change(&mut r2);
    }

    fn change(s: &mut String) {
        s.push_str(", world");
    }

这里可能就是具体版本在实现上的一个差异，我用的 rustc 是 1.44.0 版本
其实上面的主要是由 rust 想要避免这类多重可变更导致的异常问题，总结下就是三个点

- 两个或两个以上的指针同时同时访问同一空间
- 其中至少有一个指针会想空间中写入数据
- 没有同步数据访问的机制
  并且我们不能在拥有不可变引用的情况下创建可变引用

  ### 悬垂引用

  还有一点需要注意的就是悬垂引用

      fn main() {
          let reference_to_nothing = dangle();
      }

      fn dangle() -> &String {
          let s = String::from("hello");
          &s
      }

  这里可以看到其实在 dangle函数返回后，这里的 s 理论上就离开了作用域，但是由于返回了 s 的引用，在 main 函数中就会拿着这个引用，就会出现如下错误

  ### 总结

  最后总结下
- 在任何一个段给定的时间里，你要么只能拥有一个可变引用，要么只能拥有任意数量的不可变引用。
- 引用总是有效的。

## 聊聊厦门旅游的好与不好

发表于 2021-04-11 分类于 生活 ， 旅游 阅读次数： Disqus：

这几天去了趟厦门，原来几年前就想去了，本来都请好假了，后面因为一些事情没去成，这次刚好公司组织，就跟 LD 一起去了厦门，也不洋洋洒洒地写游记了，后面可能会有，今天先来总结下好的地方和比较坑的地方。
这次主要去了中山路、鼓浪屿、曾厝(cuo)垵、植物园、灵玲马戏团，因为住的离环岛路比较近，还有幸现场看了下厦门马拉松，其中

## 中山路

这里看上去是有点民国时期的建筑风格，部分像那种电视里的租界啥的，不过这次去的时候都在翻修，路一大半拦起来了，听导游说这里往里面走有个局口街，然后上次听前同事说厦门比较有名的就是沙茶面和海蛎煎，不出意料的不太爱吃，沙茶面比较普通，可能是没吃到正宗的，海蛎煎吃不惯，倒是有个大叔的沙茶里脊还不错，在局口街那，还有小哥在那拍，应该也算是个网红打卡点了，然后吃了个油条麻糍也还不错，总体如果是看建筑的话可能最近不是个好时间，个人也没这方面爱好，吃的话最好多打听打听沙茶面跟海蛎煎哪里正宗。如果不知道哪家好吃，也不爱看这类建筑的可以排个坑。

## 鼓浪屿

鼓浪屿也是完全没啥概念，需要乘船过去，但是只要二十分钟，岛上没有机动车，基本都靠走，有几个比较有名的地方，菽庄花园，里面有钢琴博物馆，对这个感兴趣的可以去看看，旁边是沙滩还可以逛逛，然后有各种博物馆，风琴啥的，岛上最大的特色是巷子多，道听途说有三百多条小巷，还有几个网红打卡点，周杰伦晴天墙，还有个最美转角，都是挤满了人排队打卡拍照，不过如果不着急，慢慢悠悠逛逛还是不错的，比较推荐，推荐值☆☆

## 曾厝垵

一直读不对这个字，都是叫：那个曾什么垵，愧对语文老师，这里到算是意外之喜，鼓浪屿回来已经挺累了，不过由于比较饿（什么原因后面说），并且离住的地方不远，就过去逛了逛，东西还蛮好吃的，芒果挺便宜，一大杯才十块，无骨鸡爪很贵，不是特别爱，臭豆腐不错的，也不算很贵，这里想起来，那边八婆婆的豆乳烧仙草还不错的，去中山路那会喝了，来曾厝垵也买了，奶茶爱好者可以试试，含糖量应该很高，不爱甜食或者减肥的同学慎重考虑好了再尝试，晚上那边从牌坊出来，沿着环岛路挺多夜宵店什么的，非常推荐，推荐值☆☆☆☆

## 植物园

植物园还是挺名副其实的，有热带植物，沙漠多肉，因为赶时间逛得不多，热带雨林植物那太多人了，都是在那拍照，而且我指的拍照都是拍人照，本身就很小的路，各种十八线网红，或者普通游客在那摆 pose 拍照，挺无语的；沙漠多肉比较惊喜，好多比人高的仙人掌，一大片的仙人球，很可恶的是好多大仙人掌上都有人刻字，越来越体会到，我们社会人多了，什么样的都有，而且不少；还看了下百花厅，但没什么特别的，可能赶时间比较着急，没仔细看，比较推荐，推荐值☆☆☆

## 灵玲马戏团

对这个其实比较排斥，主要是比较晚了，跑的有点远（我太懒了），一开始真的挺拉低体验感受的，上来个什么书法家，现场画马，卖画；不过后面的还算值回票价，主题是花木兰，空中动作应该很考验基本功，然后那些老外的飞轮还跳绳（不知道学名叫啥），动物那块不太忍心看，应该是吃了不少苦头，不过人都这样就往后点再心疼动物吧。

## 总结

厦门是个非常适合干饭人的地方，吃饭的地方大部分是差不多一桌菜十个左右就完了，而且上来就一大碗饭，一瓶雪碧一瓶可乐，对于经常是家里跟亲戚吃饭都得十几二十个菜的乡下人来说，不太吃得惯这样的🤦‍♂️，当然很有可能是我们预算不足，点的差。但是有一点是我回杭州深有感触的，感觉杭州司机的素质真的是跟厦门的司机差了比较多，杭州除非公交车停了，否则人行道很难看到主动让人的，当然这里拿厦门这个旅游城市来对比也不是很公平，不过这也是体现城市现代化文明水平的一个维度吧。

## 聊聊 dubbo 的线程池

发表于 2021-04-04 分类于 Java ， Dubbo - 线程池 ， Dubbo ， 线程池 ， ThreadPool 阅读次数： Disqus：

之前没注意到这一块，只是比较模糊的印象 dubbo 自己基于 ThreadPoolExecutor 定义了几个线程池，但是没具体看过，主要是觉得就是为了避免使用 jdk 自带的那几个(java.util.concurrent.Executors)，防止出现那些问题
看下代码目录主要是这几个

- FixedThreadPool：创建一个复用固定个数线程的线程池。
  简单看下代码

      public Executor getExecutor(URL url) {
              String name = url.getParameter("threadname", "Dubbo");
              int threads = url.getParameter("threads", 200);
              int queues = url.getParameter("queues", 0);
              return new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS, (BlockingQueue)(queues == 0 ? new SynchronousQueue() : (queues < 0 ? new LinkedBlockingQueue() : new LinkedBlockingQueue(queues))), new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
          }

  可以看到核心线程数跟最大线程数一致，也就是说就不会在核心线程数和最大线程数之间动态变化了
- LimitedThreadPool：创建一个线程池，这个线程池中线程个数随着需要量动态增加，但是数量不超过配置的阈值的个数，另外空闲线程不会被回收，会一直存在。

      public Executor getExecutor(URL url) {
              String name = url.getParameter("threadname", "Dubbo");
              int cores = url.getParameter("corethreads", 0);
              int threads = url.getParameter("threads", 200);
              int queues = url.getParameter("queues", 0);
              return new ThreadPoolExecutor(cores, threads, 9223372036854775807L, TimeUnit.MILLISECONDS, (BlockingQueue)(queues == 0 ? new SynchronousQueue() : (queues < 0 ? new LinkedBlockingQueue() : new LinkedBlockingQueue(queues))), new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
          }

  这个特点主要是创建了保活时间特别长，即可以认为不会被回收了
- EagerThreadPool ：创建一个线程池，这个线程池当所有核心线程都处于忙碌状态时候，创建新的线程来执行新任务，而不是把任务放入线程池阻塞队列。

      public Executor getExecutor(URL url) {
              String name = url.getParameter("threadname", "Dubbo");
              int cores = url.getParameter("corethreads", 0);
              int threads = url.getParameter("threads", 2147483647);
              int queues = url.getParameter("queues", 0);
              int alive = url.getParameter("alive", 60000);
              TaskQueue<Runnable> taskQueue = new TaskQueue(queues <= 0 ? 1 : queues);
              EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor(cores, threads, (long)alive, TimeUnit.MILLISECONDS, taskQueue, new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
              taskQueue.setExecutor(executor);
              return executor;
          }

  这个是改动最多的一个了，因为需要实现这个机制，有兴趣的可以详细看下
- CachedThreadPool： 创建一个自适应线程池，当线程处于空闲1分钟时候，线程会被回收，当有新请求到来时候会创建新线程

      public Executor getExecutor(URL url) {
              String name = url.getParameter("threadname", "Dubbo");
              int cores = url.getParameter("corethreads", 0);
              int threads = url.getParameter("threads", 2147483647);
              int queues = url.getParameter("queues", 0);
              int alive = url.getParameter("alive", 60000);
              return new ThreadPoolExecutor(cores, threads, (long)alive, TimeUnit.MILLISECONDS, (BlockingQueue)(queues == 0 ? new SynchronousQueue() : (queues < 0 ? new LinkedBlockingQueue() : new LinkedBlockingQueue(queues))), new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
          }

  这里可以看到线程池的配置，核心是 0，最大线程数是 2147483647，保活时间是一分钟
  只是非常简略的介绍下，有兴趣可以自行阅读代码。

## 2020 年终总结

发表于 2021-03-31 分类于 生活 ， 年终总结 ， 2020 ， 年终总结 ， 2020 阅读次数： Disqus：

## 拖更原因

这篇年终总结本来应该在农历过完年就出来的，结果是对没有受疫情影响的春节放假时间空闲情况预估太良好，虽然公司调了几天假，但是因为春节期间疫情状况比较好，本来酒店都不让接待聚餐什么的，后来统统放开，结果就是从初一到初六每天要不就是去亲戚家，要不就是去酒店饭店吃饭，计划很丰满，现实很骨感，时间感觉一下就没了，然后年后感觉有点犯懒了，所以才拖到现在。

## 生活-健身跑步

去年（19 年）的时候跑步突破了 300 公里，然后20 年给自己定了个 400 公里的目标，结果意料之中的没成功，原因可能疫情算一点吧，后面买了跑步机之后，基本周末回家都能跑一下，但是最后还是只跑了300 多公里，总的keep 记录跑量也没超过 1000 公里，所以跑步这个目标还是没成功的，不过还算是比去年多跑一点，这样也算后面好突破点，后面的目标就不定的太高了，每年能比前一年多一点就好，其实跑步已经从一种减肥方式变成一种习惯了，一周一次的跑步已经比较难有效减重了，但是对于保持精力和身体状态还是很有效和重要的，只是对于目前的体重还是要多减下去一些跑步才好，太重了对膝盖负担太大了，可惜还是时间呐，游泳骑车什么的都需要更苛刻的条件和时间，饮食呢控制起来比较难（贪吃
终于在 3 月底之前跑到了 1000 公里，迟了三个月，不过也总算达到了，只是体重控制还是不行，有试着走走楼梯，但是感觉对膝盖负担比较大，得再想想用什么方式

## 技术成长

一直提不起笔来写这篇年终总结还有个比较大的原因是觉得20 年的成长不如预期，大小目标都没怎么完成，比如深入了解 jvm，是想能有些深入的见解，而不再是某些点的比较片面的理解，系统性的归纳总结也比较少，每个方向或多或少有些看法和理解，但是不全面，一些东西看过了也会忘记，需要温故而知新，比如 AQS 的内容，第一次读其实理解比较浅，后面就强迫自己去读，去写，才有了一些比之前更深入的理解，因为很多文章都是带有作者思路的引导，适不适合自己都要看是否能从他的思路把它看懂，有些就差别很大，这个跟看书也一样，有些书大众一致推荐，一般情况下大多是经典的好的，但是也有可能是不太适合自己的，可能有时候机缘巧合看到的反而让人茅塞顿开，在 todo 里已经积攒了好多的点和面需要去学习实践，一方面是自己懒，一方面是时间也相对偏少，看看 21 年能不能有所提升，加强“时间管理”，哈哈

技术上主要是看了 mysql 的 mvcc 相关内容，rocketmq 的，redis 的代码，还有 mybatis 等，其实每一个都能写很多，也有很多值得学习的，需要全面系统学习，之前想好好画一个思维导图，将整个技术体系都梳理下，还只做了一点点，方式也有点问题，应该从大到小，而不是深度优先，细节有很多，每一个方面都有自己比较熟悉擅长的，也有不太了解的，可以做一个评分，这个也是亟待改善的，希望今年能完成。

## 博客

博客方面 20 年一年整是写了 53 篇，差不多是一周一篇的节奏，这个还是不错的，虽然博客质量参差不齐，但是这个更新频率还是比较好的，并且也定了个潜规则，可以一周技术一周生活，这样能缓解水文的频率，提高些技术文章的质量，虽然结果并没有好多少，不过感觉还是可以这么坚持的，能提高一些技术文章的质量那就更好了