JDK1.5以后加入了concurrent包,主要是为了提高多线程的开发效率,其中提供了很多支持并发的集合类,其中包括:ConcurrentHashMap。大家知道HashTable也是支持并发环境的,也就是说多线程安全的,那两者有什么区别呢?
分析
其实简单的说是同步机制有区别,具体区别又在那里呢?
请看HashTable的put方法:
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<K,V>(hash, key, value, e);
count++;
return null;
}
代码中使用synchronized函数的方式进行同步,这个类的其他方法也是使用这个机制进行同步的。我们先来大致了解一下synchronized的机制:
在多线程环境中,Java的对象都会隐式的包含有一个同步队列,其中类会有一个,然后每个类实例也会包含一个。如图:
class Foo {
synchronized void doSomething(); // 把同步的线程放入类实例的同步队列
synchronized static void doSomething(); //把同步的线程放入类的同步队列
}
然后我们再来看看 的put方法:
//ConcurrentHashMap
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, value, false);
}
//Segment内部类,继承至ReentrantLock
V put(K key, int hash, V value, boolean onlyIfAbsent) {
lock();
try {
int c = count;
if (c++ > threshold) // ensure capacity
rehash();
HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
HashEntry<K,V> first = tab[index];
HashEntry<K,V> e = first;
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
V oldValue;
if (e != null) {
oldValue = e.value;
if (!onlyIfAbsent)
e.value = value;
}
else {
oldValue = null;
++modCount;
tab[index] = new HashEntry<K,V>(key, hash, first, value);
count = c; // write-volatile
}
return oldValue;
} finally {
unlock();
}
}
ReentrantLock就是Java Concurrent包提供的锁对象,Lock的使用方法大致如下:
为什么使用Lock对象会比使用synchronized有更好的性能呢?我们再来看看ReentrantLock的实现:
我们从中看到ReentrantLock对象也维护着一个同步队列,性能差别就在于这个队列的实现上,我们再来看AbstractQueuedSynchronizer的代码:
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
Node h = new Node(); // Dummy header
h.next = node;
node.prev = h;
if (compareAndSetHead(h)) {
tail = node;
return h;
}
}
else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
/**
* CAS head field. Used only by enq
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);//使用compare and swap方式
}
/**
* CAS tail field. Used only by enq
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);//使用compare and swap方式
}
到了Unsafe.java这里就要通过本地代码实现了,下面是kaffe里面的本地代码实现:
/**
* Helper macro, defining a sun.misc.Unsafe compare and swap function
* with a given NAME tail and TYPE of arguments.
*/
#define KAFFE_UNSAFE_COMPARE_AND_SWAP(NAME, TYPE)
JNIEXPORT jboolean JNICALL Java_sun_misc_Unsafe_compareAndSwap ## NAME(JNIEnv* env, jobject unsafe UNUSED, jobject obj, jlong offset, TYPE expect, TYPE update)
{
volatile TYPE * address = getFieldAddress(env, obj, offset);
if (sizeof(TYPE) == sizeof(gint))
return g_atomic_int_compare_and_exchange((volatile gint *) address, (gint) expect, (gint) update);
else if (sizeof(TYPE) == sizeof(gpointer))
return g_atomic_pointer_compare_and_exchange((volatile gpointer *) address, (gpointer) expect, (gpointer) update);
else
if (*address == expect) {
*address = update;
return JNI_TRUE;
}
else
return JNI_FALSE;
}
再看glib的代码:
gboolean g_atomic_int_compare_and_exchange (volatile gint *atomic,
gint oldval,
gint newval)
{
gint result;
__asm__ __volatile__ ("lock; cmpxchgl %2, %1"
: "=a" (result), "=m" (*atomic)
: "r" (newval), "m" (*atomic), "0" (oldval));
return result == oldval;
}
AT&T汇编
一条指令总会是原子性的了吧
总结
concurrent包中大量使用了新的锁机制,新的Lock机制最终归结到一个原子性操作上,所以会比使用synchronized关键字有更高的性能。
分享到:
相关推荐
3. **健壮性**:Java设计时注重安全性和错误处理,例如通过强类型检查和异常处理机制。 4. **多线程**:Java内置了对多线程编程的支持,允许开发者创建同时执行的多个线程。 5. **网络编程**:Java提供了丰富的...
在Java中,内置锁是通过synchronized关键字实现的,而显示锁则是通过java.util.concurrent.locks包中的接口和类实现的。显示锁的一个重要接口是Lock,它提供了对锁操作的更细粒度控制,而ReentrantLock是Lock的一个...
"java.util.concurrent包中的线程池和消息队列" java.util.concurrent包中的线程池和消息队列是一种高效的多线程编程技术,主要用于解决处理器单元内多个线程执行的问题。线程池技术可以显著减少处理器单元的闲置...
显式锁是Java 5引入的java.util.concurrent.locks包中的Lock接口及其实现类,如ReentrantLock。与内置锁相比,显式锁提供了更多的控制选项,例如非公平锁、可中断锁、定时锁等。显式锁需要程序员手动获取和释放,这...
`java.util.concurrent.locks`包中提供了比`synchronized`关键字更强大、更灵活的锁实现——`ReentrantLock`。 **2.2.1 `ReentrantLock`特性** - 支持公平锁和非公平锁的选择。 - 提供尝试获取锁的能力,可以指定...
5. **Synchronized与Lock**:`synchronized`关键字提供了内置锁机制,确保同一时间只有一个线程访问共享资源。`concurrent`包中的`ReentrantLock`提供了更细粒度的锁控制,包括公平锁和非公平锁,以及可中断和定时...
5. **并发优化**:如果你有大量的XML数据或者复杂的转换逻辑,可以考虑使用`java.concurrent`包中的工具。例如,可以创建一个ExecutorService,将每个XML文件的转换作为一个单独的任务提交,这样多个XML文件可以并行...
`java.util.concurrent`包中的`AbstractQueuedSynchronizer`框架为Java开发者提供了一个强大的工具箱,使得他们能够高效地实现各种同步器。通过对同步状态的有效管理、阻塞和唤醒机制的优化以及灵活的扩展性设计,...
可重入锁是基于Java.util.concurrent.locks包中的ReentrantLock类,提供了比同步锁更灵活的锁定控制机制。读写锁同样在该包中,即ReadWriteLock,允许多个线程并发读取,但写入时只能一个线程进行,可以提高并发性能...
4. **Java并发工具**:Doug Lea详细介绍了java.util.concurrent包中的各种工具类,如Executor框架、Semaphore信号量、CountDownLatch倒计时器、CyclicBarrier回环栅栏和Future异步计算接口。这些工具极大地简化了...
在Java的`java.util.concurrent.locks`包中,`ReentrantReadWriteLock`类实现了读写锁的功能。这个锁允许多个读取者同时访问资源,但在有写入者时,所有读取者和写入者都会被阻塞,以确保数据的一致性。下面我们将...
使用`java.util.concurrent`包中的高级并发工具,如`Atomic`类和`Concurrent`集合,可以实现非阻塞同步,提高程序性能。 9. **volatile 关键字**: 与`synchronized`不同,`volatile`关键字主要用于确保共享变量...
AQS(AbstractQueuedSynchronizer)是Java.util.concurrent包中同步器的基础框架,它的核心设计思想与实现方法在Doug Lea先生的这篇论文中有详细的介绍。论文详细阐述了AQS框架的原理、设计、实现、应用以及性能等...
关于AbstractQueuedSynchronizer,它是java.util.concurrent包中并发控制的核心组件,为Java并发工具类提供了底层机制支持,例如ReentrantLock、Semaphore、CountDownLatch等,都依赖于这个框架来实现同步。...
Java提供了多种机制来保证线程安全,如`synchronized`关键字、`volatile`变量以及`java.util.concurrent`包中的高级并发工具类。 2. **死锁**:当两个或更多的线程在执行过程中,每个线程都在等待另一个线程释放...
书中强调了线程安全、同步、线程池、并发集合、死锁、活锁与饥饿等核心概念,以及如何使用java.util.concurrent包中的类和接口。例如,`ExecutorService`和`Future`接口,它们为管理和控制并发任务提供了便利;`...
Java.util.concurrent包中的`ConcurrentLinkedQueue`、`BlockingQueue`等数据结构可以作为事件队列,线程间通过这些队列进行异步通信。 总的来说,Java的事件机制和观察者模式为对象间的通信提供了一种解耦的方式,...
内置锁是通过synchronized关键字实现的,而显式锁则是通过java.util.concurrent.locks包中的Lock接口及其实现类来提供的。 一、内置锁(synchronized) 1. 同步方法:在方法声明前加上synchronized关键字,整个...