wordpress 文章索引 存储过程

最近想给本站加上文章索引的页面,就是在一个页面上展示所有的分类及该分类的文章,然后置顶到首页,效果见本站文章索引。开始的思路是:先新建一个页面,然后设置好置顶等,用存储过程更新该页面的内容,用触发器在有新文章发表或文章标题有更新时触发执行存储过程。看起来应该挺顺利的,不过实现过程倒没想象那么好了。

更新索引存储过程

这个存储过程的功能是从数据库里查询出所有的分类以及该分类下的文章的ID和标题,然后用游标生成html内容(wordpress直接存储页面的html内容),再更新为索引页面的内容。
继续阅读

MySQL 乱码 与 字符集

MySQL 安装后默认的字符集是 latin1,这样在处理中文时容易出现乱码问题,所以需要改为 utf8

注:我的MySQL版本是5.6.*

用语句 show variables like 'character%'; 查看MySQL当前的字符集设置(也可以使用命令 status查看):

mysql> show variables like 'character%';
+--------------------------+----------------------------------+
| Variable_name            | Value                            |
+--------------------------+----------------------------------+
| character_set_client     | latin1                           |
| character_set_connection | latin1                           |
| character_set_database   | latin1                           |
| character_set_filesystem | binary                           |
| character_set_results    | latin1                           |
| character_set_server     | latin1                           |
| character_set_system     | utf8                             |
| character_sets_dir       | /usr/share/mysql/share/charsets/ |
+--------------------------+----------------------------------+
8 rows in set (0.03 sec)

从结果可以看出,MySQL字符集涉及到:

  • 客户端使用的字符集:character_set_client
  • 客户端域服务器之间的连接使用的字符集:character_set_connection
  • 数据库实例使用的字符集:character_set_database
  • MySQL服务器使用的字符集:character_set_serve
    继续阅读

《把时间当作朋友:运用心智获得解放》笔记

《把时间当作朋友:运用心智获得解放》是 李笑来 的一本书,在微信上看到有人多次推荐,就买了电子版来看。里面讲的一些东西非常现实,现实到让人有些难以接受。摘录一些话,看了有兴趣的可以去买本看看。

不思考的人是没有问题可问的;会思考的人有问题往往并不去问人,因为他们最终能够自己解决问题。

教是最好的学习方法。

往往并不是有兴趣才能做好,而是做好了才有兴趣。

方法固然重要,但是比起“用功”来说,方法几乎可以忽略不计。(这让我想起经常看到的一句话:以大多数人的努力程度,根本轮不到拼天赋。)

所有学习上的成功,都只靠两件事:策略和坚持,而坚持本身就是最重要的策略。

与其不停地找更好的方法,还不如马上开始行动,省得虚度更多的时间。

有些哪怕是常识的东西也需要亲身经历过后才能体会。

坚持不懈就是策略加上重复。

平静接受并且正确认识自己的天性是改变天性的第一步。

见识越少的人越喜欢用自己所有的见识作为判断依据,并且完全不顾自己见识的局限,也不知道自己的见识有局限。

一个故事至少有三个版本:你的、我的、真实的。

事实上,买回来一本书里,哪怕有一句话给我们带来惊喜、带来思考、带来改变,就已经值回价值。

决定一个人富有的三个条件:出身、运气、努力,这三者中,努力是最微不足道的。(这是一个经济学家奈特说的)

我们所面临的今天很大程度取决于我们的过去,无论后悔程度多么强烈都无济于事。

我们所使用的语言往往会限制我们的思维。

耐心就是甘于把时间投入到简单、枯燥但是最终意义非凡的重复当中去。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

JUC ConcurrentLinkedQueue

java.util.concurrent.ConcurrentLinkedQueue 是一个基于链接结点的、无界、线程安全的、FIFO队列。它的是实现采用了无等待(wait-free)、无锁(lock-free)算法,该算法基于 Maged M. Michael 和 Michael L. Scott 合著的 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms 中描述的算法。

一、设计思路

  1. 使用 CAS 原子指令来处理对数据的并发访问,把同步最小化到单个硬件指令上;这是无锁算法的基础。
  2. 分别用 head、tail 两个原子指针来指向队头和队尾,可以同时进行入队和出队操作,但允许 head、tail 并不总是指向有效的头和尾;把入队、出队需要同步更新的范围最小化到单个原子变量上,这是无锁算法实现的关键。
  3. 在入队、出队操作上并不总是更新 head、tail,而是在多次操作后才进行更新,节省了CAS指令。具有批量更新的效果。
  4. 由于 head、tail 并不总是指向头和尾,这就是说,队列会出现不一致的情况,所以在 head、tail 上定义了一些不变式来维护算法的正确性。

二、不变式

不变式是在执行方法之前和之后,队列必须要保持的。可变式是在执行过程中,队列允许出现的不一致情况。

head 的约束

通过这个结点能够在 O(1) 时间到达第一个存活(非已删除)结点,如果有的话。

不变式
  • 所有存活结点可以 从 head 开始通过 succ() 访问。
  • head != null
  • (tmp = head).next != tmp || tmp != head,这个不变式是说 head 在方法开始之前、之后,head 指向的结点应该是在队列上的。
可变式
  • head.item 可能是、也可能不是 空的。
  • 允许 tail 落后于 head,那是因为 tail 不能从 head 到达。

tail 的约束

从它可以在 O(1) 时间访问到队列的最后一个结点(唯一的满足 node.next == null 的结点)。这个也就是说 tail 指向的并不总是最后一个存活结点。

不变式
  • 最后的结点总是可以从 tail 开始通过 succ() 访问。
  • tail != null
可变式
  • tail.item 可能是、也可能不是空。
  • 允许 tail 落后于 head,那是因为 tail 不能从 head 到达。
  • tail.next 可能是、也可能不是 自指向到 tail。
    继续阅读

2013 年终总结

日志

09年毕业,工作4年多,以前都没写过年度总结。今年开始要在每年年底最后一周完成该年度的总结。诚如我的QQ空间的签名“有些事不记下来就象什么也发生过”,现在想想年初做过什么已经比较模糊了,希望用文字把记忆变得清晰点。

工作

本来这份工作面试时是说做移动网盘(叫彩云)后台的,结果各种原因项目留在南京,接不回来,新建的团队没有可以长期做的项目,做了几个小项目后在去年底就解散了。今年转到另一个项目组,主要是做彩云的web portal,也就是彩云的web客户端,这个东西每隔2、3个月就大改一次,要不然我早失业了。职责主要是写JS做业务展现、写一些Java代码调后台接口,开发速度相对还是比较快的,而且人也多,不会出现一个人负责很多模块。所以,上班时可以支配的时间还是不少的。

自我学习

既然上班都有可以支配的时间,自然还是要学点东西的。

总体情况

今年在工具的使用上有很大进步。早上上班前、中午都会刷下微博,可以了解下业界的新东西、别人的分享等等。学习了Markdown书写法,用印象笔记做了大量的笔记,搭建了自己的个人博客,到目前累计发表了94篇文章,访问量也过万了,还是挺满意的。8月份买了kindle,看了几本电子书,效果很赞的。早上搭公车也用微信看一些公共帐号。feedly的订阅更多了,要有选择地阅读。
继续阅读

JUC LinkedBlockingQueue

java.util.concurrent.LinkedBlockingQueue 是一个基于单向链表的、范围任意的(其实是有界的)、FIFO 阻塞队列。访问与移除操作是在队头进行,添加操作是在队尾进行,并分别使用不同的锁进行保护,只有在可能涉及多个节点的操作才同时对两个锁进行加锁。

队列是否为空、是否已满仍然是通过元素数量的计数器(count)进行判断的,由于可以同时在队头、队尾并发地进行访问、添加操作,所以这个计数器必须是线程安全的,这里使用了一个原子类 AtomicInteger,这就决定了它的容量范围是: 1 – Integer.MAX_VALUE。

由于同时使用了两把锁,在需要同时使用两把锁时,加锁顺序与释放顺序是非常重要的:必须以固定的顺序进行加锁,再以与加锁顺序的相反的顺序释放锁。

头结点和尾结点一开始总是指向一个哨兵的结点,它不持有实际数据,当队列中有数据时,头结点仍然指向这个哨兵,尾结点指向有效数据的最后一个结点。这样做的好处在于,与计数器 count 结合后,对队头、队尾的访问可以独立进行,而不需要判断头结点与尾结点的关系。
继续阅读

JUC ArrayBlockingQueue

java.util.concurrent.ArrayBlockingQueue 是一个线程安全的、基于数组、有界的、阻塞的、FIFO 队列。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

此类基于 java.util.concurrent.locks.ReentrantLock 来实现线程安全,所以提供了 ReentrantLock 所能支持的公平性选择。

属性

队列的操作主要有读、写,所以用了两个 int 类型的属性作为下一个读写位置的的指针。存放元素的数组是 final 修饰的,所以数组的大小是固定的。对于并发控制,是所有的访问都必须加锁,并用两个条件对象用于协调读写操作。

// 队列存放元素的容器
final Object[] items;

// 下一次读取或移除的位置
int takeIndex;

// 存放下一个放入元素的位置
int putIndex;

// 队列里有效元素的数量
int count;


// 所有访问的保护锁
final ReentrantLock lock;

// 等待获取的条件
private final Condition notEmpty;

// 等待放入的条件
private final Condition notFull;

环绕处理

如果指针一直往前增加或一直往后减小,那么总会超出数组的有效索引范围。所以需要进行一些环绕处理。

// 指针前移
final int inc(int i) {
    return (++i == items.length) ? 0 : i;
}

// 指针后移
final int dec(int i) {
    return ((i == 0) ? items.length : i) - 1;
}

注意,上面的处理都是对指针值的直接处理,而不关心是读指针还是写指针,因为是否有可读元素、可写空间的判断是通过对 count 计数来判断的。

这也是 count 的作用,它极大地简化了指针有效性的判断。在下面的 insertextract 方法中根本就不需要对读写指针之间的位置关系进行判断,非常精妙。

通过环绕处理可以把这个数组看成是圆形的缓存。
继续阅读

I/O 基础

缓冲区操作

缓冲区以及缓冲区是如何工作,是所有I/O的基础。“输入/输出”就是把数据移进或移出缓冲区。

进程执行I/O操作,就是向操作系统发出请求,让它要么把缓冲区的数据排干(写),要么用数据把缓冲区填满(读)。进程使用这一机制处理所有数据进出操作。

从磁盘读数据到进程内存区:
read-from-disk-into-user-process

进程使用 read( ) 系统调用,要求其缓冲区被填满。内核随即向磁盘控制硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据直接写入内核内存缓冲区,这一步通过DMA完成,无需主CPU 协助。一旦磁盘控制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进程执行 read( ) 调用时指定的缓冲区。

用户空间与内核空间

  • 用户空间:用户空间是常规进程所在区域,是非特权区域,比如该区域的代码不能直接访问硬件设备。
  • 内核空间:内核空间是操作系统所在区域,有特别的权利:能与设备控制器通讯,控制用户区域进程的运行状态等等。

所有I/O都直接或间接通过内核空间,通过请求页面调度完成。

当进程请求I/O操作的时候,它执行一个系统调用将控制权移交给内核。内核随即采取必要步骤,找到进程所需数据,并把数据传送到用户空间内指定的缓冲区。如果数据已在内核空间,直接拷贝即可;如果不在内核空间,则进程被挂起,内核着手把数据读进内存。

发散、汇聚

根据发散、汇聚的概念,进程只需一个系统调用,就能把一连串缓冲区地址传递给操作系统,然后内核就可以顺序填充或排干多个缓冲区,读的时候把数据发散到多个用户空间缓冲区,写的时候再从多个缓冲区把数据汇聚起来。

scatter-gather

继续阅读

JUC 源码分析 四 wait notify notifyAll 与 条件对象

内置锁 与 wait notify 机制

每个Java对象都有一个内置锁,通过 synchronized 关键字使用。线程之间可以通过 Object 类的 wait, notify, notifyAll 进行协调。

wait, notify, notifyAll 方法说明:

  • wait:在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。
  • notify:唤醒在此对象监视器上等待的单个线程,直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意性的,被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。
  • notifyAll:唤醒在此对象监视器上等待的所有线程,直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。

以一个有界缓存为例,展示了内置锁和 wait、notify、notifyAll 的一般用法:

public class BoundedBuffer<T> {
       private final Object[] buffer;
       private final int length;

       public BoundedBuffer(int length ) {
             if (length < 0) {
                   throw new IllegalArgumentException("length < 0");
            }
             this.length = length ;
             buffer = new Object[length ];
      }

       // synchronized 用于方法上,表示一个同步方法,线程进入方法前自动获得内置锁
       public synchronized void put(T obj) throws InterruptedException {
             // 线程被唤醒时,条件不一定满足(虚假唤醒),所以需要在循环里进行测试、等待
             while (isFull()) {
                   // 在当前的对象实例上等待,由其他线程调用 notifyAll 或 notify 方法唤醒
                  wait();
            }

            doPut(obj);

             // 唤醒在此对象监视器上等待(通过wait方法进入等待)的所有线程。
             // 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。
            notifyAll();

      }

       public synchronized T take() throws InterruptedException {
            T object = null;
             while (isEmpty()) {
                  wait();
            }
            object = doTake();
            notifyAll();
             return object;
      }

       private void doPut(T obj) { // not implemented
      }

       private T doTake() { // not implemented
             return null ;
      }

       private boolean isEmpty() { // not implemented
             return false ;
      }

       private boolean isFull() { // not implemented
             return false ;
      }
}

继续阅读