計算機是由電路構成的,電路只有0和1 兩種狀態。程序員
在十進制中,個位的1表明10⁰=1,十位的1表明10¹=10,百位的1表明10²=100,因此:123=1×10²+2×10¹+3×10⁰算法
一樣道理,在二進制中,個位的1表明2⁰=1,十位的1表明2¹=2,百位的1表明2²=4,因此:(A3A2A1A0)₂=A3×2³+A2×2²+A1×2¹+A0×2⁰編程
若是二進制和十進制數出如今同一個等式中,爲了區別咱們用(A3A2A1A0)₂這種形式表示A3A2A1A0是二進制數,每一個數字只能是0或1,其它沒有套括號加下標的數仍表示十進制數。對於(A3A2A1A0)₂這樣一個二進制數,最左邊的A3位稱爲最高位(MSB,Most Significant Bit),最右邊的A0位稱爲最低位(LSB,Least Significant Bit)。之後咱們遵循這樣的慣例:LSB稱爲第0位而不是第1位,因此若是一個數是32位的,則MSB是第31位。上式就是從二進制到十進制的換算公式。bash
下面來看十進制怎麼換算成二進制。咱們知道ide
13=1×2³+1×2²+0×2¹+1×2⁰ 因此13換算成二進制應該是(1101)₂。問題是怎麼把13分解成等號右邊的形式呢?注意到等號右邊能夠寫成優化
13=(((0×2+1₃)×2+1₂)×2+0₁)×2+1₀ui
咱們將13反覆除以2取餘數就能夠提取出上式中的1101四個數字,爲了讓讀者更容易看清楚是哪一個1和哪一個0,上式和下式中對應的數字都加了下標:spa
13÷2=6...1₀ 6÷2=3...0₁ 3÷2=1...1₂ 1÷2=0...1₃debug
把這四步獲得的餘數按相反的順序排列就是13的二進制表示,所以這種方法稱爲除二反序取餘法。code
計算機用二進制表示數,程序員也必須習慣使用二進制,但二進制寫起來太囉嗦了,因此一般將二進制數分紅每三位一組或者每四位一組,每組用一個數字表示。好比把(10110010)₂從最低位開始每三位分紅一組,十、1十、010,而後把每組寫成一個十進制數字,就是(262)₈,這樣每一位數字的取值範圍是0~7,逢八進一,稱爲八進制(Octal)。相似地,把(10110010)₂分紅每四位一組,10十一、0010,而後把每組寫成一個數字,這個數的低位是2,高位已經大於9了,咱們規定用字母A~F表示10~15,這個數能夠寫成(B2)₁₆,每一位數字的取值範圍是0~F,逢十六進一,稱爲十六進制(Hexadecimal)。因此,八進制和十六進制是程序員爲了書寫二進制方便而發明的簡便寫法,比如草書和正楷的關係同樣。
整數在計算機中用二進制的位來表示,C語言提供一些運算符能夠直接操做整數中的位,稱爲位運算。
移位運算符(Bitwise Shift)包括左移<<和右移>>。左移將一個整數的各二進制位所有左移若干位,例如0xcfffffff3<<2獲得0x3fffffcc:
Java 平臺都是有符號整數,因此上述圖一操做在Java中符號位發生了變化值由(-805306381)變爲(1073741772)
在必定的取值範圍內,將一個整數左移1位至關於乘以2。好比二進制11(十進制3)左移一位變成110,就是6,再左移一位變成1100,就是12。讀者能夠本身驗證這條規律對有符號數和無符號數都成立,對負數也成立。固然,若是左移改變了最高位(符號位),那麼結果確定不是乘以2了,因此我加了個前提「在必定的取值範圍內」。因爲計算機作移位比作乘法快得多,編譯器能夠利用這一點作優化,好比看到源代碼中有i * 8,能夠編譯成移位指令而不是乘法指令。
當操做數是無符號數時,右移運算的規則和左移相似,例如0xcffffff3>>2獲得0x33fffffc:
Java 平臺執行結果:值由-805306381 變成 -201326596 仍然保留負數的符號位,至關於除以4
最低兩位的11被移出去了,最高兩位又補了兩個0,其它位依次右移兩位。和左移相似,移動的位數也必須小於左操做數的總位數,不然結果是Undefined。在必定的取值範圍內,將一個整數右移1位至關於除以2,小數部分截掉。
當操做數是有符號數時,右移運算的規則比較複雜:
綜上所述,因爲類型轉換和移位等問題,用有符號數作位運算是很不方便的,因此,建議只對無符號數作位運算,以減小出錯的可能。
若是要對一個整數中的某些位進行操做,怎樣表示這些位在整數中的位置呢?能夠用掩碼(Mask)來表示。好比掩碼0x0000ff00表示對一個32位整數的8~15位進行操做,舉例以下。
一、取出8~15位。
unsigned int a, b, mask = 0x0000ff00;
a = 0x12345678;
b = (a & mask) >> 8; /* 0x00000056 */
複製代碼
二、將8~15位清0。
unsigned int a, b, mask = 0x0000ff00;
a = 0x12345678;
b = a & ~mask; /* 0x12340078 */
複製代碼
三、將8~15位置1。
unsigned int a, b, mask = 0x0000ff00;
a = 0x12345678;
b = a | mask; /* 0x1234ff78 */
複製代碼
shrding-jdbc默認的實現:
/**
* 默認的主鍵生成器.
*
* <p>
* 長度爲64bit,從高位到低位依次爲
* </p>
*
* <pre>
* 1bit 符號位
* 41bits 時間偏移量從2016年11月1日零點到如今的毫秒數
* 10bits 工做進程Id
* 12bits 同一個毫秒內的自增量
* </pre>
*
* <p>
* 工做進程Id獲取優先級: 系統變量{@code sharding-jdbc.default.key.generator.worker.id} 大於 環境變量{@code SHARDING_JDBC_DEFAULT_KEY_GENERATOR_WORKER_ID}
* ,另外能夠調用@{@code DefaultKeyGenerator.setWorkerId}進行設置
* </p>
*
* @author gaohongtao
*/
@Getter
@Slf4j
public final class DefaultKeyGenerator implements KeyGenerator {
public static final long EPOCH;
public static final String WORKER_ID_PROPERTY_KEY = "sharding-jdbc.default.key.generator.worker.id";
public static final String WORKER_ID_ENV_KEY = "SHARDING_JDBC_DEFAULT_KEY_GENERATOR_WORKER_ID";
private static final long SEQUENCE_BITS = 12L;
private static final long WORKER_ID_BITS = 10L;
private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;
private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS;
private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS;
private static final long WORKER_ID_MAX_VALUE = 1L << WORKER_ID_BITS;
@Setter
private static TimeService timeService = new TimeService();
@Getter
private static long workerId;
static {
Calendar calendar = Calendar.getInstance();
calendar.set(2016, Calendar.NOVEMBER, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
EPOCH = calendar.getTimeInMillis();
initWorkerId();
}
private long sequence;
private long lastTime;
public static void initWorkerId() {
String workerId = System.getProperty(WORKER_ID_PROPERTY_KEY);
if (!Strings.isNullOrEmpty(workerId)) {
setWorkerId(Long.valueOf(workerId));
return;
}
workerId = System.getenv(WORKER_ID_ENV_KEY);
if (Strings.isNullOrEmpty(workerId)) {
return;
}
setWorkerId(Long.valueOf(workerId));
}
/**
* 設置工做進程Id.
*
* @param workerId 工做進程Id
*/
public static void setWorkerId(final long workerId) {
Preconditions.checkArgument(workerId >= 0L && workerId < WORKER_ID_MAX_VALUE);
DefaultKeyGenerator.workerId = workerId;
}
/**
* 生成Id.
*
* @return 返回@{@link Long}類型的Id
*/
@Override
public synchronized Number generateKey() {
long currentMillis = timeService.getCurrentMillis();
Preconditions.checkState(lastTime <= currentMillis, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastTime, currentMillis);
if (lastTime == currentMillis) {
if (0L == (sequence = ++sequence & SEQUENCE_MASK)) {
currentMillis = waitUntilNextTime(currentMillis);
}
} else {
sequence = 0;
}
lastTime = currentMillis;
if (log.isDebugEnabled()) {
log.debug("{}-{}-{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(lastTime)), workerId, sequence);
}
return ((currentMillis - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (workerId << WORKER_ID_LEFT_SHIFT_BITS) | sequence;
}
private long waitUntilNextTime(final long lastTime) {
long time = timeService.getCurrentMillis();
while (time <= lastTime) {
time = timeService.getCurrentMillis();
}
return time;
}
}
複製代碼
小尾巴走一波,歡迎關注個人公衆號,不按期分享編程、投資、生活方面的感悟:)