`
NumberRbb
  • 浏览: 70888 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

由hashtable、hashMap到线程同步

    博客分类:
  • java
 
阅读更多

Hashtable 与 hashMap 区别于两点:1.前者是线程同步的 2.是前者不允许出现null键和null值。尽而说到线程同步:

用什么关键字修饰同步方法 ? synchronized关键字修饰同步方法

 同步有几种实现方法,都是什么?分别是synchronized,waitnotify

wait():使一个线程处于等待状态,并且释放所持有的对象的lock
sleep():
使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():
唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():
唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

实现同步的方式

同步是多线程中的重要概念。同步的使用可以保证在多线程运行的环境中,程序不会产生设计之外的错误结果。同步的实现方式有两种,同步方法和同步块,这两种方式都要用到synchronized关键字。

给一个方法增加synchronized修饰符之后就可以使它成为同步方法,这个方法可以是静态方法和非静态方法,但是不能是抽象类的抽象方法,也不能是接口中的接口方法。下面代码是一个同步方法的示例:

public synchronized void aMethod() { 

    // do something 

} 

public static synchronized void anotherMethod() { 

    // do something 

} 

线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。

同步块是通过锁定一个指定的对象,来对同步块中包含的代码进行同步;而同步方法是对这个方法块里的代码进行同步而这种情况下锁定的对象就是同步方法所属的主体对象自身。如果这个方法是静态同步方法呢?那么线程锁定的就不是这个类的对象了,也不是这个类自身,而是这个类对应的java.lang.Class类型的对象。同步方法和同步块之间的相互制约只限于同一个对象之间,所以静态同步方法只受它所属类的其它静态同步方法的制约,而跟这个类的实例(对象)没有关系。

如果一个对象既有同步方法,又有同步块,那么当其中任意一个同步方法或者同步块被某个线程执行时,这个对象就被锁定了,其他线程无法在此时访问这个对象的同步方法,也不能执行同步块。

synchronized 关键字用于保护共享数据。请大家注意“共享数据”,你一定要分清哪些数据是共享数据,请看下面的例子:

public class ThreadTest implements Runnable{

public synchronized void run(){

for(int i=0;i<10;i++) {

System.out.print(" " + i);

}

}

public static void main(String[] args) {

Runnable r1 = new ThreadTest(); //也可写成ThreadTest r1 = new ThreadTest();

Runnable r2 = new ThreadTest();

Thread t1 = new Thread(r1);

Thread t2 = new Thread(r2);

t1.start();

t2.start();

}}

在这个程序中,run()虽然被加上了synchronized 关键字,但保护的不是共享数据。因为这个程序中的t1,t2 两个对象(r1,r2)的线程。而不同的对象的数据是不同的r1,r2 有各自的run()方法,所以输出结果无法预知。

synchronized的目的是使同一个对象的多个线程在某个时刻只有其中的一个线程可以访问这个对象的synchronized 数据每个对象都有一个“锁标志”,当这个对象的一个线程访问这个对象的某个synchronized 数据时,这个对象的所有被synchronized 修饰的数据将被上锁(因为“锁标志”被当前线程拿走了),只有当前线程访问完它要访问的synchronized 数据时,当前线程才会释放“锁标志”,这样同一个对象的其它线程才有机会访问synchronized 数据。

示例3

public class ThreadTest implements Runnable{

public synchronized void run(){

for(int i=0;i<10;i++){

System.out.print(" " + i);

}

}

public static void main(String[] args){

Runnable r = new ThreadTest();

Thread t1 = new Thread(r);

Thread t2 = new Thread(r);

t1.start();

t2.start();

}}

如果你运行1000 次这个程序,它的输出结果也一定每次都是:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9。因为这里的synchronized 保护的是共享数据。t1,t2 是同一个对象(r)的两个线程,当其中的一个线程(例如:t1)开始执行run()方法时,由于run()synchronized保护,所以同一个对象的其他线程(t2)无法访问synchronized 方法(run 方法)。只有当t1执行完后t2 才有机会执行。

示例4

public class ThreadTest implements Runnable{

public void run(){

synchronized(this){

for(int i=0;i<10;i++){

System.out.print(" " + i);

}} }

public static void main(String[] args){

Runnable r = new ThreadTest();

Thread t1 = new Thread(r);

Thread t2 = new Thread(r);

t1.start();

t2.start();

}}

这个程序与示例3 的运行结果一样。在可能的情况下,应该把保护范围缩到最小,可以用示例4 的形式,this 代表“这个对象”。没有必要把整个run()保护起来,run()中的代码只有一个for循环,所以只要保护for 循环就可以了。

示例5

public class ThreadTest implements Runnable{

public void run(){

for(int k=0;k<5;k++){

System.out.println(Thread.currentThread().getName()+ " : for loop : " + k);

}

synchronized(this){

for(int k=0;k<5;k++) {

System.out.println(Thread.currentThread().getName()+ " : synchronized for loop : " + k);

}} }

public static void main(String[] args){

Runnable r = new ThreadTest();

Thread t1 = new Thread(r,"t1_name");

Thread t2 = new Thread(r,"t2_name");

t1.start();

t2.start();

} }

运行结果:

t1_name : for loop : 0

t1_name : for loop : 1

t1_name : for loop : 2

t2_name : for loop : 0

t1_name : for loop : 3

t2_name : for loop : 1

t1_name : for loop : 4

t2_name : for loop : 2

t1_name : synchronized for loop : 0

t2_name : for loop : 3

t1_name : synchronized for loop : 1

t2_name : for loop : 4

t1_name : synchronized for loop : 2

t1_name : synchronized for loop : 3

t1_name : synchronized for loop : 4

t2_name : synchronized for loop : 0

t2_name : synchronized for loop : 1

t2_name : synchronized for loop : 2

t2_name : synchronized for loop : 3

t2_name : synchronized for loop : 4

第一个for 循环没有受synchronized 保护。对于第一个for 循环,t1,t2 可以同时访问。运行结果表明t1 执行到了k=2 时,t2 开始执行了。t1 首先执行完了第一个for 循环,此时t2还没有执行完第一个for 循环(t2 刚执行到k=2t1 开始执行第二个for 循环,当t1的第二个for 循环执行到k=1 时,t2 的第一个for 循环执行完了。t2 想开始执行第二个for 循环,但由于t1 首先执行了第二个for 循环,这个对象的锁标志自然在t1 手中(synchronized 方法的执行权也就落到了t1 手中),在t1 没执行完第二个for 循环的时候,它是不会释放锁标志的。所以t2 必须等到t1 执行完第二个for 循环后,它才可以执行第二个for 循环。

分享到:
评论

相关推荐

    比较Vector、ArrayList和hashtable hashmap

    - HashMap 和 Hashtable 都实现了 Map 接口,HashMap 更快但不是线程安全的,而 Hashtable 是线程安全但较慢。WeakHashMap 则使用弱引用作为键,有助于防止内存泄漏。 - 在选择使用哪种数据结构时,需要考虑性能需求...

    hashtable和hashmap的区别

    - **HashMap**: 非同步设计使得其在单线程环境下的性能优于`Hashtable`。然而,在多线程环境下,可以通过`Collections.synchronizeMap()`方法将`HashMap`转换成线程安全的版本。此外,从Java 5开始,推荐使用`...

    关于如何解决HashMap线程安全问题的介绍

    HashMap线程不安全的原因主要在于以下几个方面: 1. 多线程环境下并发修改:在多线程环境下,如果多个线程同时对HashMap进行读写操作,可能会导致数据的不一致性和数据丢失。例如,当一个线程正在执行put操作时,另...

    HashMap和HashTable的区别和不同

    为了在多线程环境中安全地使用`HashMap`,开发者需要自己负责同步,例如使用`Collections.synchronizedMap(new HashMap,V&gt;())`创建线程安全的`HashMap`实例。 #### 2. 允许null值 - **HashTable**: 不支持`null`键...

    HashMap与HashTable的区别(含源码分析)

    在Java编程语言中,`HashMap`和`HashTable`都是实现键值对存储的数据结构,但它们之间存在一些显著的区别,这些区别主要体现在线程安全性、性能、null值处理以及一些方法特性上。以下是对这两个类的详细分析: 1. ...

    经典讲解List和ArrayList和Vector和HashTable和HashMap区别

    - **线程安全性**:`Vector`和`HashTable`是线程安全的,而`ArrayList`和`HashMap`不是。在多线程环境下,`ArrayList`和`HashMap`需要通过同步机制来保证数据一致性。 - **存储方式**:`List`接口的实现(如`...

    Hashtable和HashMap的区别:

    不过,`HashMap` 的同步问题可以通过 `Collections.synchronizedMap()` 方法得到解决,该方法可以将 `HashMap` 包装成一个线程安全的 `Map` 实例。 #### 三、null 值处理 - **HashMap**:允许使用 `null` 键和 `...

    HashMap与HashTable区别

    如果多个线程同时访问一个`HashMap`实例,且至少有一个线程修改了该`HashMap`,则必须通过外部同步来保证线程安全。例如,可以通过将`HashMap`对象包装在一个`Collections.synchronizedMap()`返回的对象中来实现这...

    hashMap和hashTable的区别

    - **HashMap**:由于没有同步操作,因此在单线程环境中通常比 `HashTable` 性能更好。 - **HashTable**:虽然提供了线程安全,但这也意味着每次调用方法时都会发生同步操作,这可能会导致性能下降。 4. **初始化...

    hashmap与hashtable区别

    这是因为它内部的方法都是同步化的(synchronized),这就意味着在多线程环境中使用`Hashtable`时无需额外的同步处理。 - **HashMap**:默认情况下,`HashMap`是非线程安全的。如果需要在一个多线程环境中使用`...

    Hashtable和HashMap区别

    相比之下,`HashMap`是非线程安全的,它没有同步任何方法,因此在多线程环境中使用时,可能需要显式地添加同步控制,例如通过`Collections.synchronizedMap()`方法来创建一个线程安全的`Map`。 #### 3. 允许null...

    HashMap与HashTable和HashSet的区别

    - **线程安全性**:`HashMap`不是线程安全的,如果多个线程同时访问,必须在外部进行同步控制。 - **null的支持**:`HashMap`允许一个键为`null`,且允许多个值为`null`。 - **初始容量和加载因子**:`HashMap`同样...

    HashMap底层实现原理HashMap与HashTable区别HashMap与HashSet区别.docx

    HashMap是非同步的,意味着在多线程环境中,如果不进行适当的同步控制,可能会导致数据不一致。而HashTable是同步的,因此它在多线程环境下的安全性更高,但这也牺牲了性能。此外,HashMap允许键和值为null,而...

    HashMap,HashTable,ConcurrentHashMap之关联.docx

    ConcurrentHashMap 是 Java 中的另一个线程安全的类,它与 HashTable 在线程同步上有什么不同?ConcurrentHashMap 使用了分段锁的机制,每个段都独立锁定,提高了并发性能。 HashMap 和 HashTable 的区别 1. 线程...

    hashmap和hashtable的区别.docx

    - 在单线程环境中,HashMap 比 Hashtable 快,因为没有内置的同步开销。 - 在多线程环境下,如果不需要全局同步,使用 ConcurrentHashMap 可以获得比 Hashtable 更好的性能。 6. 方法名称: - Hashtable 使用了 ...

    HashMap,HashTable,LinkedHashMap,TreeMap的区别

    HashMap 不支持线程的同步,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。如果需要同步,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有同步的能力。 LinkedHashMap ...

    HashMap源码分析系列-第四弹:HashMap多线程解决方案.docx

    #### 二、HashMap线程安全问题分析 在多线程环境中,`HashMap`的主要线程安全问题包括但不限于: 1. **链表死循环问题**:在JDK 1.7中,当多个线程同时进行`put`操作时,可能会出现链表死循环的情况,这是一个严重...

    Java中Hashtable类与HashMap类的区别详解

    - `HashMap`由于不需要线程同步,通常在单线程环境中比`Hashtable`更快。 - 在多线程环境下,`Hashtable`的同步特性保证了数据一致性,但牺牲了效率。对于高并发场景,`HashMap`需要配合`synchronized`关键字或`...

    第9讲 对比Hashtable、HashMap、TreeMap有什么不同?1

    由于同步机制的存在,Hashtable的性能相比HashMap较低,现在在多线程需求下,通常更推荐使用ConcurrentHashMap,它在并发性能上做了优化。此外,Hashtable不支持null键和null值。 TreeMap是一种基于红黑树的Map实现...

    Java中List、ArrayList、Vector及map、HashTable、HashMap分别的区别.

    与HashMap相比,HashTable的同步特性使得它在多线程环境下更安全,但在单线程环境下,由于同步开销,其性能较低。 5. LinkedList与ArrayList的比较 LinkedList是List接口的另一个实现,它基于双向链表实现,对于在...

Global site tag (gtag.js) - Google Analytics