Java多线程编程概述
Java多线程的安全问题
Java多线程同步
Java多线程间的通信
Java线程Lock
Java多线程管理
保障线程安全的设计技术
Java锁的优化及注意事项
Java多线程集合
【Java多线程】单例模式与多线程

Java线程同步的集合

ArrayList,HashSet,HashMap等集合不是线程安全的.在多线程环境中使用这些集合可能会出现数据不一致的情况,或者产生异常。

Vector集合,HashTable集合是线程安全,在这些集合中使用synchronized关键字把方法修饰为同步方法,只允许由一个线程调用其中一个方法, 所以又把Vector,HashTable集合称为同步集合。

在Collections工具类中提供了一组synchronizedXXX()方法可以把不是线程安全的集合转换为线程安全的集合,这也是同步集合。

package com.wkcto.syncColleciton;

import java.util.ArrayList;
import java.util.Vector;

/**
 * 并发下的ArrayList集合
 */
public class Test01 {
    //创建ArrayList集合
//    private static ArrayList<Integer> arrayList = new ArrayList<>();
    //创建Vector集合
    private static Vector<Integer> arrayList = new Vector<>();

    //定义线程类,在该线程中不断的向ArrayList集合中添加元素
    private static class  AddDataThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                arrayList.add( i );
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动三个线程,向ArrayList集合中添加数据
        AddDataThread t1 = new AddDataThread();
        AddDataThread t2 = new AddDataThread();
        AddDataThread t3 = new AddDataThread();

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
        System.out.println( arrayList.size() );
        /*
            当程序运行后,会产生异常Exception in thread "Thread-2" Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: Index 48 out of bounds for length 15
            ArrayList底层是使用数组来存储数据的,在向ArrayList集合中添加元素时,如果元素的数量超过底层数组的长度,数组需要扩容, 在扩容过程中,没有对数组进行保护,在多线程环境中可能会出现一致性被破坏的情况,一个线程在扩容,另外一个线程在存储数据,访问了不一致的内部状态,导致了数组的越界
            还有一个可能出现的问题,最终集合的容量可能不准确,这也是多线程访问冲突造成的
            解决方法:
                在List集合中还有实现类是Vector,它的底层也是数组,它是线程安全的,把ArrayList换成Vector即可
                Vector中的操作都使用了synchronized关键字把方法修饰为同步方法, 即在多线程环境 中,只能有某一个线程调用它的某个方法, 所以 也称Vector集合为同步集合
         */
    }
}
package com.wkcto.syncColleciton;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * HashMap不是线程安全的
 */
public class Test02 {
    //创建HashMap集合,存储<整数,整数的二进制形式>
//    private static Map<String,String> map = new HashMap<>();
    //使用线程安全的HashTable存储
    private static Map<String,String> map = new Hashtable<>();

    //定义线程类,向map中添加数据
    private static class AddDataThread extends Thread{
        private int start = 0 ;

        public AddDataThread(int start) {
            this.start = start;
        }

        @Override
        public void run() {
            //通过循环把奇数与偶数分开
            for (int i = start; i < start+ 100000; i+=2) {
                //把整数,整数的二进制添加到map中
                map.put(Integer.toString(i), Integer.toBinaryString(i));
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //创建线程把从0开始的偶数及对应的二进制添加到map中
        AddDataThread t1 = new AddDataThread(0);
        //创建线程把从1开始的奇数及对应的二进制添加到map中
        AddDataThread t2 = new AddDataThread(1);

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        //两个线程添加完成后,查看map中键值对的数量
        System.out.println(map.size());
        /*
            运行程序,输出map的键值对数量,它可能是一个小于100000的数字,即出现了数据不一致的线程安全问题
            解决方法:
                可以把HashMap集合换成HashTable
                在HashTable集合中,使用synchronized关键字把操作修饰为同步方法, 即多线程环境中只允许由一个线程来操作HashTable集合,它是线程安全的, 也把HashTable称为同步集合
         */
    }
}