博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用JDBC处理Oracle大数据
阅读量:5943 次
发布时间:2019-06-19

本文共 21153 字,大约阅读时间需要 70 分钟。

大数据也称之为LOB(Large Objects),LOB又分为:clob和blob

clob字符型大型对象用于存储大文本,如历史档案、大部头著作等;blob二进制大型对象用于存储二进制数据,如程序、图象、影音等。

  在实际开发中,有时是需要用程序把大文本或二进制数据直接保存到数据库中进行储存的。

 

使用JDBC处理Oracle大数据总结

 

  通过JDBC操纵Oracle数据库的LOB字段,不外乎插入、修改、替换、读取四种方式,掌握起来并不难。观察上述程序对LOB类型字段的存取,我们可以看出,较之其它类型字段,有下面几个显著不同的特点

 

  1、必须取消自动提交

 

  存取操作开始前,必须用setAutoCommit(false)取消自动提交。其它类型字段则无此特殊要求。这是因为存取LOB类型字段时,通常要进行多次操作可以完成。不这样的话,Oracle将抛出“读取违反顺序”的错误。

 

  2、插入方式不同

 

  LOB数据不能象其它类型数据一样直接插入(INSERT)。插入前必须先插入一个空的LOB对象,CLOB类型 的空对象为EMPTY_CLOB(),BLOB类型的空对象为EMPTY_BLOB()。之后通过SELECT命令查询得到先前插入的记录并锁定,继而将 空对象修改为所要插入的LOB对象。

 

  3、修改方式不同

 

  其它类型的字段修改时,用UPDATE … SET…命令即可。而LOB类型字段,则只能用SELECT … FOR UPDATE命令将记录查询出来并锁定,然后才能修改。且修改也有两种改法:一是在原数据基础上的修改(即覆盖式修改),执行SELECT … FOR UPDATE后再改数据;二是替换(先将原数据清掉,再修改),先执行UPDATE命令将LOB字段之值设为空的LOB对象,然后进行第一种改法。建议使 用替换的方法,以实现与其它字段UPDATE操作后一样的效果。

 

  4、存取时应使用由数据库JDBC驱动程序提供的LOB操作类

 

  对于Oracle数据库,应使用oracle.sql.CLOB和oracle.sql.BLOB。不使用由数据库JDBC驱动程序提供的LOB类时,程序运行时易于出现“抽象方法调用”的错误,这是因为JDBC所定义的java.sql.Clob与 java.sql.Blob接口,其中的一些方法并未在数据库厂家提供的驱动程序中真正实现。

 

  5、存取手段与文件操作相仿

 

  对于BLOB类型,应用InputStream/OutputStream类,此类不进行编码转换,逐个字节存取。oracle.sql.BLOB类相应提供了getBinaryStream()和getBinaryOutputStream()两个方法,前一个 方法用于读取Oracle的BLOB字段,后一个方法用于将数据写入Oracle的BLOB字段。

 

  对于CLOB类型,应用Reader/Writer类,此类进行编码转换。oracle.sql.CLOB类相应 提供了getCharacterStream()和getCharacterOutputStream()两个方法,前一个方法用于读取Oracle的 CLOB字段,后一个方法用于将数据写入Oracle的CLOB字段。

 

  需要说明的是,为了大幅提高程序执行效率,对BLOB/CLOB字段的读写操作,应该使用缓冲操作类(带 Buffered前缀),即:BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。

实例:

编写db.properties配置文件

oracleDb_Driver=oracle.jdbc.driver.OracleDriveroracleDb_Url=jdbc:oracle:thin:@localhost:1521:GACLoracleDb_UserName=GACL_XDPoracleDb_Password=P
db.properties配置文件 Code

 编写JdbcUtils工具类

package me.gacl.utils;import java.io.InputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;public class JdbcUtils {    private static String oracleDb_Driver = null;    private static String oracleDb_Url = null;    private static String oracleDb_UserName = null;    private static String oracleDb_Password = null;        static{        try{            //读取db.properties文件中的数据库连接信息            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");            Properties prop = new Properties();            prop.load(in);                        //获取数据库连接驱动            oracleDb_Driver = prop.getProperty("oracleDb_Driver");            //获取数据库连接URL地址            oracleDb_Url = prop.getProperty("oracleDb_Url");            //获取数据库连接用户名            oracleDb_UserName = prop.getProperty("oracleDb_UserName");            //获取数据库连接密码            oracleDb_Password = prop.getProperty("oracleDb_Password");                        //加载数据库驱动            Class.forName(oracleDb_Driver);                    }catch (Exception e) {            throw new ExceptionInInitializerError(e);        }    }        /**    * @Method: getOracleConnection    * @Description: 获取Oracle数据库连接对象    * @Anthor:孤傲苍狼    *    * @return Connection数据库连接对象    * @throws SQLException    */     public static Connection getOracleConnection() throws SQLException{        return DriverManager.getConnection(oracleDb_Url, oracleDb_UserName,oracleDb_Password);    }        /**    * @Method: release    * @Description: 释放资源,    *     要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象    * @Anthor:孤傲苍狼    *    * @param conn    * @param st    * @param rs    */     public static void release(Connection conn,Statement st,ResultSet rs){        if(rs!=null){            try{                //关闭存储查询结果的ResultSet对象                rs.close();            }catch (Exception e) {                e.printStackTrace();            }            rs = null;        }        if(st!=null){            try{                //关闭负责执行SQL命令的Statement对象                st.close();            }catch (Exception e) {                e.printStackTrace();            }        }                if(conn!=null){            try{                //关闭Connection数据库连接对象                conn.close();            }catch (Exception e) {                e.printStackTrace();            }        }    }}
JdbcUtils工具类 View Code

1、JDBC处理CLOB数据

JdbcOperaOracleClob.java
package me.gacl.demo;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.FileWriter;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import org.junit.Test;import me.gacl.utils.JdbcUtils;/*** @ClassName: JdbcOperaOracleClob* @Description:Oracle中字符型大型对象(Character Large Object)数据处理* @author: 孤傲苍狼* @date: 2014-10-7 下午3:53:19**/ public class JdbcOperaOracleClob {    /**    CREATE TABLE TEST_CLOB ( ID NUMBER(3), CLOBCOL CLOB)     */    /**    * @Method: clobInsert    * @Description:往数据库中插入一个新的CLOB对象    * @Anthor:孤傲苍狼    *    * @throws Exception    */     @Test    public void clobInsert() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        conn = JdbcUtils.getOracleConnection();        boolean defaultCommit = conn.getAutoCommit();        /*开启事务,设定不自动提交 */        conn.setAutoCommit(false);        try {            /* 插入一个空的CLOB对象 */            String sql = "INSERT INTO TEST_CLOB VALUES (?, EMPTY_CLOB())";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            stmt.executeUpdate();            /* 查询此CLOB对象并锁定 */            sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                /* 取出此CLOB对象 */                oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");                /* 向CLOB对象中写入数据 */                BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream());                //这种方式获取的路径,其中的空格会被使用“%20”代替                String path = JdbcOperaClob.class.getClassLoader().getResource("data.txt").getPath();                //将“%20”替换回空格                path = path.replaceAll("%20", " ");                BufferedReader in = new BufferedReader(new FileReader(path));                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                in.close();                out.close();            }            /* 正式提交 */            conn.commit();            System.out.println("插入成功");        } catch (Exception ex) {            /* 出错回滚 */            conn.rollback();            throw ex;        }finally{            /* 恢复原提交状态 */            conn.setAutoCommit(defaultCommit);            JdbcUtils.release(conn,stmt,rs);        }            }        /**    * @Method: clobRead    * @Description: CLOB对象读取    * @Anthor:孤傲苍狼    *    * @throws Exception    */    @Test    public void clobRead() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        conn = JdbcUtils.getOracleConnection();        boolean defaultCommit = conn.getAutoCommit();        conn.setAutoCommit(false);        try {            /* 查询CLOB对象 */            String sql = "SELECT * FROM TEST_CLOB WHERE ID=?";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                /* 获取CLOB对象 */                oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");                /* 以字符形式输出 */                BufferedReader in = new BufferedReader(clob.getCharacterStream());                BufferedWriter out = new BufferedWriter(new FileWriter("D:\\2.txt"));                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                out.close();                in.close();            }        } catch (Exception ex) {            conn.rollback();            throw ex;        }finally{            /* 恢复原提交状态 */            conn.setAutoCommit(defaultCommit);            JdbcUtils.release(conn,stmt,rs);        }    }        /**    * @Method: clobModify    * @Description:修改CLOB对象(是在原CLOB对象基础上进行覆盖式的修改)    * @Anthor:孤傲苍狼    *    * @throws Exception    */     @Test    public void clobModify() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        conn = JdbcUtils.getOracleConnection();        boolean defaultCommit = conn.getAutoCommit();        // 开启事务        conn.setAutoCommit(false);        try {            /* 查询CLOB对象并锁定 */            String sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                /* 获取此CLOB对象 */                oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");                /* 进行覆盖式修改 */                BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream());                // 这种方式获取的路径,其中的空格会被使用“%20”代替                String path = JdbcOperaClob.class.getClassLoader().getResource("data2.txt").getPath();                // 将“%20”替换回空格                path = path.replaceAll("%20", " ");                BufferedReader in = new BufferedReader(new FileReader(path));                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                in.close();                out.close();            }            /*提交事务 */            conn.commit();        } catch (Exception ex) {            /*出错回滚事务 */            conn.rollback();            throw ex;        }finally{            /*恢复原提交状态 */            conn.setAutoCommit(defaultCommit);            JdbcUtils.release(conn,stmt,rs);        }    }    /**    * @Method: clobReplace    * @Description:替换CLOB对象(将原CLOB对象清除,换成一个全新的CLOB对象)    * @Anthor:孤傲苍狼    *    * @throws Exception    */     @Test    public void clobReplace() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        conn = JdbcUtils.getOracleConnection();        boolean defaultCommit = conn.getAutoCommit();        // 开启事务        conn.setAutoCommit(false);        try {            /* 清空原CLOB对象 */            String sql = "UPDATE TEST_CLOB SET CLOBCOL=EMPTY_CLOB() WHERE ID=?";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            stmt.executeUpdate();            /* 查询CLOB对象并锁定 */            sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                /* 获取此CLOB对象 */                oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");                /* 更新数据 */                BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream());                // 这种方式获取的路径,其中的空格会被使用“%20”代替                String path = JdbcOperaClob.class.getClassLoader().getResource("db.properties").getPath();                // 将“%20”替换回空格                path = path.replaceAll("%20", " ");                BufferedReader in = new BufferedReader(new FileReader(path));                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                in.close();                out.close();            }            /* 正式提交 */            conn.commit();        } catch (Exception ex) {            /* 出错回滚 */            conn.rollback();            throw ex;        } finally {            /* 恢复原提交状态 */            conn.setAutoCommit(defaultCommit);            JdbcUtils.release(conn, stmt, rs);        }    }}
View Code
 

2、JDBC处理BLOB数据

Oracle定义了一个BLOB字段用于保存二进制数据,但这个字段并不能存放真正的二进制数据,只能向这个字段存一个指针,然后把数据放到指针所指向的Oracle的LOB段中, LOB段是在数据库内部表的一部分。因而在操作Oracle的Blob之前,必须获得指针(定位器)才能进行Blob数据的读取和写入。

  如何获得表中的Blob指针呢? 可以先使用insert语句向表中插入一个空的blob(调用oracle的函数empty_blob()),这将创建一个blob的指针,然后再把这个empty的blob的指针查询出来,这样就可得到BLOB对象,从而读写blob数据了。

  1、插入空blob:insert into testblob(id,image) values(?,empty_blob())

  2、获得blob的cursor:

    select image from testblob where id=? for update 注意: 必 须加for update锁定该行,直至该行被修改完毕,保证不产生并发冲突。

    Blob b = rs.getBlob("image");

    
  3、利用 io和获取到的cursor往数据库读写数据
  注意:以上操作需开启事务。

JDBC对BLOB对象的存取实例

package me.gacl.demo;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.FileOutputStream;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import org.junit.Test;import me.gacl.utils.JdbcUtils;/*** @ClassName: JdbcOperaOracleBlob* @Description:Oracle中大数据处理* @author: 孤傲苍狼* @date: 2014-10-7 下午3:53:19**/ public class JdbcOperaOracleBlob {    /**     * @Method: blobInsert     * @Description: 向数据库中插入一个新的BLOB对象     * @Anthor:孤傲苍狼     *      * @throws Exception     */    @Test    public void blobInsert() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        boolean defaultCommit = true;        try {            conn = JdbcUtils.getOracleConnection();            //得到数据库事务处理的默认提交方式            defaultCommit = conn.getAutoCommit();            //1、开启事务            conn.setAutoCommit(false);            //2、插入一个空的BLOB对象            String sql = "INSERT INTO TEST_BLOB VALUES (?, EMPTY_BLOB())";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            stmt.executeUpdate();            //3、查询此BLOB对象并锁定。注意: 必 须加for update锁定该行,直至该行被修改完毕,保证不产生并发冲突            sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                //4、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象                oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");                //5、使用IO向BLOB对象中写入数据                BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream());                BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("01.jpg"));                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                in.close();                out.close();            }            //6、提交事务            conn.commit();        } catch (Exception ex) {            //7、出错回滚事务            conn.rollback();            throw ex;        } finally {            //8、恢复数据库事务处理的默认提交方式            conn.setAutoCommit(defaultCommit);            //释放资源            JdbcUtils.release(conn, stmt, rs);        }    }    /**     * @Method: blobModify     * @Description:修改BLOB对象(是在原BLOB对象基础上进行覆盖式的修改)     * @Anthor:孤傲苍狼     *      * @throws Exception     */    @Test    public void blobModify() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        boolean defaultCommit = true;        try {            conn = JdbcUtils.getOracleConnection();            //得到数据库事务处理的默认提交方式            defaultCommit = conn.getAutoCommit();            //1、开启事务            conn.setAutoCommit(false);            //2、查询此BLOB对象并锁定。注意: 必 须加for update锁定该行,直至该行被修改完毕,保证不产生并发冲突            String sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                //3、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象                oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");                //4、使用IO向BLOB对象中写入数据                BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream());                BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("02.jpg"));                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                in.close();                out.close();            }            //5、提交事务            conn.commit();        } catch (Exception ex) {            //6、出错回滚事务            conn.rollback();            throw ex;        } finally {            //8、恢复数据库事务处理的默认提交方式            conn.setAutoCommit(defaultCommit);            //释放资源            JdbcUtils.release(conn, stmt, rs);        }    }    /**     * @Method: blobReplace     * @Description:替换BLOB对象(将原BLOB对象清除,换成一个全新的BLOB对象)     * @Anthor:孤傲苍狼     *      * @throws Exception     */    @Test    public void blobReplace() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        boolean defaultCommit = true;        try {            conn = JdbcUtils.getOracleConnection();            //得到数据库事务处理的默认提交方式            defaultCommit = conn.getAutoCommit();            //1、开启事务            conn.setAutoCommit(false);            //2、清空原BLOB对象            String sql = "UPDATE TEST_BLOB SET BLOBCOL=EMPTY_BLOB() WHERE ID=?";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            stmt.executeUpdate();            //3、查询此BLOB对象并锁定。注意: 必 须加for update锁定该行,直至该行被修改完毕,保证不产生并发冲突            sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                //4、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象                oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");                //5、使用IO向BLOB对象中写入数据                BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream());                BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("01.jpg"));                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                in.close();                out.close();            }            //6、提交事务            conn.commit();        } catch (Exception ex) {            //7、出错回滚事务            conn.rollback();            throw ex;        } finally {            //8、恢复数据库事务处理的默认提交方式            conn.setAutoCommit(defaultCommit);            //释放资源            JdbcUtils.release(conn, stmt, rs);        }    }    /**     * @Method: blobRead     * @Description:BLOB对象读取     * @Anthor:孤傲苍狼     *      * @throws Exception     */    @Test    public void blobRead() throws Exception {        Connection conn = null;        PreparedStatement stmt = null;        ResultSet rs = null;        boolean defaultCommit = true;        try {            conn = JdbcUtils.getOracleConnection();            //得到数据库事务处理的默认提交方式            defaultCommit = conn.getAutoCommit();            //1、开启事务            conn.setAutoCommit(false);            //2、查询BLOB对象            String sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=?";            stmt = conn.prepareStatement(sql);            stmt.setInt(1, 1);            rs = stmt.executeQuery();            if (rs.next()) {                //3、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象                oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");                //4、以二进制流的形式输出                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("D:/1.jpg"));                BufferedInputStream in = new BufferedInputStream(blob.getBinaryStream());                int c;                while ((c = in.read()) != -1) {                    out.write(c);                }                in.close();                out.close();            }            //5、提交事务            conn.commit();        } catch (Exception ex) {            //6、出错回滚事务            conn.rollback();            throw ex;        } finally {            //8、恢复数据库事务处理的默认提交方式            conn.setAutoCommit(defaultCommit);            //释放资源            JdbcUtils.release(conn, stmt, rs);        }    }}
JdbcOperaOracleBlob View Code

Clob类型转为String字符串

 

// 将字CLOB转成STRING类型           public String ClobToString(Clob clob) throws SQLException, IOException {                             String reString = "";               java.io.Reader is = clob.getCharacterStream();// 得到流               BufferedReader br = new BufferedReader(is);               String s = br.readLine();               StringBuffer sb = new StringBuffer();               while (s != null) {
// 执行循环将字符串全部取出付值给StringBuffer由StringBuffer转成STRING sb.append(s); s = br.readLine(); } reString = sb.toString(); return reString; }

 

Blob类型转为String字符串

 

//把数据库中blob类型转换成String类型,即转换成InputStream,再从InputStream转成byte[],再到String即可。 public String convertBlobToString(Blob blob){    String result = "";  try {   ByteArrayInputStream msgContent =(ByteArrayInputStream) blob.getBinaryStream();   byte[] byte_data = new byte[msgContent.available()];   msgContent.read(byte_data, 0,byte_data.length);   result = new String(byte_data);  } catch (SQLException e) {
  e.printStackTrace(); } return result; }

 

 

转载于:https://www.cnblogs.com/huangdabing/p/9187841.html

你可能感兴趣的文章
2.3系列系统中不支持SimpleDateFormat作字段被序列化
查看>>
DJANGO MODEL FORMSETS IN DETAIL AND THEIR ADVANCED USAGE
查看>>
ADO.NET复习——自己编写SqlHelper类
查看>>
库函数strlen源码重现及注意问题
查看>>
java读取数据,2,2,1方式读取
查看>>
《实例化需求》读书笔记
查看>>
正则表达式
查看>>
Springmvc ajax请求400
查看>>
HDFS集群安装部署
查看>>
WorkFlow For Net ! NET 平台工作流 或 BPM
查看>>
****Curling 2.0(深搜+回溯)
查看>>
小题目【链表1】
查看>>
常用Java8语法小结
查看>>
ZJOI2019 Day2 游记
查看>>
ccf题库中2015年12月2号消除类游戏
查看>>
WinForm窗体间如何传值
查看>>
作业9月30号
查看>>
【JS基础】循环
查看>>
Ado.Net 连接数据库
查看>>
java多线程系列1:Sychronized关键字
查看>>