ThreadLocal的实际应用
0.问题场景
在学习Hbase的时候,发现有很多操作数据的方法,就想着把他封装起来,以便其他的类也使用,于是按照常规操作就创建了一个Util类,
public class HbaseUtils {
private HbaseUtils(){
}
private static Connection connection = null;
public static Connection getConnection() throws IOException {
//0.创建配置对象,获取hbase的连接
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","hadoop132");
conf.set("hbase.zookeeper.property.clientPort","2181");
//1.获取hbase连接对象
connection = ConnectionFactory.createConnection(conf);
return connection;
}
public static void insertData(String tableName,String rowKey,String family,String column,String value) throws IOException {
Connection connection = connHolder.get();
Table table = connection.getTable(TableName.valueOf(tableName));
Put put = new Put(Bytes.toBytes(rowKey));
put.addColumn(Bytes.toBytes(family),Bytes.toBytes(column),Bytes.toBytes(value));
table.put(put);
table.close();
}
public static void close() throws IOException {
connection.close();
}
}
很容易就可以写完一个操作,但是这个工具类在仔细研究后发现是有问题的。比如当有main函数调用这个工具类,就有可能会出现多线程访问的情况,就会出现线程不安全的问题,当有多个线程访问这个main是时候,因为采用工具类只创建一个Connection可能就会出现这样的问题,就是当thread1进来创建一个连接,thread2过来使用这个连接,当thread1结束操作,就关闭连接而thread2还在使用连接,这就导致线程安全的问题。
1.问题解决
其实这个问题就是解决线程不安全的问题,实际上就是在于thread1线程和thread2线程用的同一个连接,要是两个线程过来,不是使用的同一个连接,那么就不会出现这样的问题了,思路有了,实现起来也不是很困难,就是将Connection在调用的时候创建就行了,不用采用公共的Connection就可以解决。
public class HbaseUtils {
private HbaseUtils(){
}
public static Connection getConnection() throws IOException {
//0.创建配置对象,获取hbase的连接
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","hadoop132");
conf.set("hbase.zookeeper.property.clientPort","2181");
//1.获取hbase连接对象
Connection connection = ConnectionFactory.createConnection(conf);
return connection;
}
public static void insertData(String tableName,String rowKey,String family,String column,String value) throws IOException {
Connection connection = connHolder.get();
Table table = connection.getTable(TableName.valueOf(tableName));
Put put = new Put(Bytes.toBytes(rowKey));
put.addColumn(Bytes.toBytes(family),Bytes.toBytes(column),Bytes.toBytes(value));
table.put(put);
table.close();
}
public static void close(Connection connection) throws IOException {
connection.close();
}
}
这样就解决了线程不安全的问题,虽然可以解决但是我们能不能用采用头一钟写法,去解决线程不安全的问题呢?
这个时候就用到了ThreadLocal了。
ThreadLocal,顾名思义就是线程本地变量,他为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。也就是说我们将Connection连接只在当前的线程中创建,并创建和关闭操作在同一线程内,这样使得每个线程会在自己独立的变量副本中创建connection。
public class HbaseUtils {
private static ThreadLocal<Connection> connHolder = new ThreadLocal<Connection>();
private HbaseUtils(){
}
public static void makeConnection() throws IOException {
Connection connection = connHolder.get();
if (connection == null){
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","hadoop132");
conf.set("hbase.zookeeper.property.clientPort","2181");
connection = ConnectionFactory.createConnection(conf);
connHolder.set(connection);
}
}
public static void insertData(String tableName,String rowKey,String family,String column,String value) throws IOException {
Connection connection = connHolder.get();
Table table = connection.getTable(TableName.valueOf(tableName));
Put put = new Put(Bytes.toBytes(rowKey));
put.addColumn(Bytes.toBytes(family),Bytes.toBytes(column),Bytes.toBytes(value));
table.put(put);
table.close();
}
public static void close() throws IOException {
Connection connection = connHolder.get();
if (connection != null){
connection.close();
//需要丢弃掉
connHolder.remove();
}
}
}

浙公网安备 33010602011771号