mit6.824

lab1 已经完工,但是遵循

Collaboration Policy

Please do not publish your code or make it available to current or future 6.824 students. github.com repositories are public by default, so please don’t put your code there unless you make the repository private. You may find it convenient to use MIT’s GitHub, but be sure to create a private repository.

所以我的代码就不开源了,所有的笔记我会在学习完所有课程,做完四个 lab 后整理后开源。

upd: 做完第一个 mapreduce 后就没时间做其他的了…

Java中的锁优化(1)

部分内容参考《深入理解Java虚拟机》第五部分——高效并发

锁优化

高效并发是JDK1.5-JDK1.6的一个重要改进,HotSpot虚拟机团队在开发这个版本的时候花了大量的精力去实现各种锁优化技术,比如适应性自旋,锁消除,锁粗化,轻量级锁和偏向锁等,这些技术都是为了在线程之间更加高效的共享数据,以及解决竞争问题。这篇文章将以概念介绍为主。

自旋锁与自适应自旋

我们知道往往在讨论锁的互斥同步的时候,其对性能最大的损耗就是阻塞的实现,挂起线程和回复线程都要进入操作系统内核态完成。然而其实有些阻塞只是很短的时间,没有必要挂起线程,所以这个时候可以选择让线程进入一个忙循环,知道阻塞条件结束,这就是所谓的自旋锁,关于自旋的代码可以参照我的这篇文章——Java原子性操作以及不如来手写一个简单锁,由于自旋是不断重试的过程,要是一直重试失败,就会造成CPU资源浪费,所以自旋需要设置次数;而所谓自适应自旋就是指根据前一次在同一个锁上的自旋时间,以及锁的拥有者的状态来判断自旋次数或是否自旋,这些在JDK1.6中都已经帮我们实现了。

Leetcode的几道有意思的多线程题

前记

最近一直在看Java的多线程编程,但苦于这东西有点过于偏向理论,结果今天上了Leetcode上看了下,发现居然有多线程相关的题目,(虽然只有五题),顺便放个链接吧,题目链接。题目感觉就是让你使用一些同步工具类或者锁或其他机制来完成要求,挺有意思的.

Java中的对象锁(Monitor)

Java中的对象锁

说到对象锁,要与之对比就不得不讲到类锁。在写单例模式的时候其实经常会用在双重校验锁中用到类锁,如下面的代码

public ClassLock {
priavte volatiled ClassLock classLock;

private ClassLock() {
// init
}

public ClassLock getInstance() {
if (classLock == null) {
synchronized (ClassLock.class) {
if (classLock == null) {
classLock = new ClassLock();
}
}
}
return classLock;
}
}

可以看到在上面的代码中我们是对一整个类进行加锁,包括直接在方法签名上使用synchronized关键字,这都是属于类锁的范畴。因为在调用一个类的实例的时候,当运行其中一个加锁的方法时,其他加锁的方法是不可以执行的。也就是说一个类其实只有一个类锁,它锁住的对象是一个类。但是对象锁不一样,一个类中可以有多个对象,也就是说对象锁在一个类的实例中不止一个, 而每个对象都可以成为一个Monitor。对象锁往往用于多个线程之间的协作,与之配套的工具是synchronized,wait,notify,notifyAll。

Java的几种同步工具类(闭锁的实现)

什么是闭锁

闭锁是一种同步工具类,可以延迟线程的进度知道其进入终止状态。闭锁的作用就像是一扇门,在闭锁结束状态之前,这扇门就是关闭的,没有任何线程可以通过。当这扇门开启的时候,闭锁将不会改变状态,会一直维持开启的状态。闭锁可以用来确保某些活动直到其他活动都完成后才可以继续执行。

CountDownLatch实现

CountDownLatch主要有两个重要的方法。

// 次数减去1
#countDown();
// 等待直到次数为0
#await();

下面的栗子体现了闭锁的等待作用

public static Long timeTasks(Integer taskNum) throws InterruptedException {
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(taskNum);
for (int i = 0; i < taskNum; i++) {
int finalI = i;
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() +" is ready");
startGate.await();
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " thread is start");
} finally {
endGate.countDown();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}

long start = System.currentTimeMillis();
// 在主线程中一起释放
startGate.countDown();
endGate.await();
long end = System.currentTimeMillis();
return end - start;
}

并发容器

并发容器与同步容器

同步容器类包括Vector与HashTable,他们实现线程安全的方法是——将他们的状态封装起来,并会每一个公有方法都是用synchronize进行同步,使得每次只有一个线程可以访问容器的状态。但是同步容器同样存在着不安全因素,所以需要客户端加锁的行为,详情可以看我的上篇博客

而相比对于并发容器来说,同步容器将所有对容器状态的访问都串行化,以保证线程安全性,但是也严重严重降低了并发性。而并发容器是针对多个线程并发访问设计的,

  • 比如使用ConcurrentHashMap来代替HashMap,ConcurrentHashMap实现了ConcurrentMap这一接口,实现了一些computeIfAbset,getOrDefault,putIfAbsent这些方法
  • Java5中增加了两种容器类型,Queue和BlockingQueue,他的并发容器提供了这么些实现包括,ConcurrentLinkedQueue(传统的FIFO队列),PriorityQueue(优先队列)。Queue一族不会阻塞,如果队列为空,那么获取元素的操作将返回空值。
  • BlockingQueue拓展了Queue,增加了可阻塞的插入和获取等操作,也就是阻塞队列。在生产者——消费者这种设计模式下,阻塞队列十分有用。

从一个容器安全类引入单例模式

使用客户端加锁的Vector的复合操作

Vector和ArrayList的线程安全性&&浅析ArrayList源码

一个例子出发

下面的一个例子展示了ArrayList的非线程安全,以及ArrayList的错误检查机制

Java对象的发布与逸出&&线程封闭机制

Java对象的发布与逸出

发布一个对象的意思是指,是对象能够在当前作用域外的代码中使用。比如Unsafe包中没有暴露一个对外的构造器,它使用的是现在类内部创建一个对象的引用,然后return 回这个引用的方式。

private static final Unsafe theUnsafe = new Unsafe();

@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}

Java原子性操作以及不如来手写一个简单锁

从i++问题的引入

在多线程编程中我们经常会看到这样一份代码

实现的计数器代码

public class Counter {
volatile int i = 0;

public void add() {
i++;
}

public int getValue() {
return this.i;
}
}

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×