提交 165492ff authored 作者: Thomas Mueller's avatar Thomas Mueller

The CompressTool was not multithreading safe.

上级 6857dfdd
...@@ -18,7 +18,10 @@ Change Log ...@@ -18,7 +18,10 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>The compression algorithm "LZF" is now about 33% faster than before when compressing small block <ul><li>The CompressTool was not multithreading safe. Because of this, the following database
operations where also not multithreading safe (even when using different databases): the SCRIPT command
(only when using compression), the COMPRESS function, and storing CLOB or BLOB data (only when compression is enabled).
</li><li>The compression algorithm "LZF" is now about 33% faster than before when compressing small block
(around 2 KB). It is much faster than Deflate, but the compression ratio is lower. (around 2 KB). It is much faster than Deflate, but the compression ratio is lower.
</li><li>Compressing large blocks of data didn't work when using the "Deflate" compression algorithm. </li><li>Compressing large blocks of data didn't work when using the "Deflate" compression algorithm.
Compressing a lot of data could run out of heap memory. Compressing a lot of data could run out of heap memory.
......
...@@ -35,7 +35,6 @@ import org.h2.util.StringUtils; ...@@ -35,7 +35,6 @@ import org.h2.util.StringUtils;
*/ */
public class CompressTool { public class CompressTool {
private static final CompressTool INSTANCE = new CompressTool();
private static final int MAX_BUFFER_SIZE = 3 * Constants.IO_BUFFER_SIZE_COMPRESS; private static final int MAX_BUFFER_SIZE = 3 * Constants.IO_BUFFER_SIZE_COMPRESS;
private byte[] cachedBuffer; private byte[] cachedBuffer;
...@@ -54,12 +53,14 @@ public class CompressTool { ...@@ -54,12 +53,14 @@ public class CompressTool {
} }
/** /**
* Get the singleton. * Get a new instance. Each instance uses a separate buffer, so multiple
* instances can be used concurrently. However each instance alone is not
* multithreading safe.
* *
* @return the singleton * @return a new instance
*/ */
public static CompressTool getInstance() { public static CompressTool getInstance() {
return INSTANCE; return new CompressTool();
} }
/** /**
...@@ -87,7 +88,7 @@ public class CompressTool { ...@@ -87,7 +88,7 @@ public class CompressTool {
/** /**
* INTERNAL * INTERNAL
*/ */
public synchronized int compress(byte[] in, int len, Compressor compress, byte[] out) { public int compress(byte[] in, int len, Compressor compress, byte[] out) {
int newLen = 0; int newLen = 0;
out[0] = (byte) compress.getAlgorithm(); out[0] = (byte) compress.getAlgorithm();
int start = 1 + writeInt(out, 1, len); int start = 1 + writeInt(out, 1, len);
......
...@@ -518,7 +518,7 @@ public abstract class TestBase { ...@@ -518,7 +518,7 @@ public abstract class TestBase {
* @param actual the actual value * @param actual the actual value
* @throws AssertionError if the values are not equal * @throws AssertionError if the values are not equal
*/ */
protected void assertEquals(byte[] expected, byte[] actual) { public void assertEquals(byte[] expected, byte[] actual) {
assertEquals(expected.length, actual.length); assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; i++) { for (int i = 0; i < expected.length; i++) {
if (expected[i] != actual[i]) { if (expected[i] != actual[i]) {
......
...@@ -36,6 +36,7 @@ public class TestCompress extends TestBase { ...@@ -36,6 +36,7 @@ public class TestCompress extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
testMultiThreaded();
if (testPerformance) { if (testPerformance) {
testDatabase(); testDatabase();
} }
...@@ -57,6 +58,44 @@ public class TestCompress extends TestBase { ...@@ -57,6 +58,44 @@ public class TestCompress extends TestBase {
testVariableEnd(); testVariableEnd();
} }
private void testMultiThreaded() throws Exception {
Thread[] threads = new Thread[3];
final boolean[] stop = new boolean[1];
final Exception[] ex = new Exception[1];
for (int i = 0; i < threads.length; i++) {
Thread t = new Thread() {
public void run() {
CompressTool tool = CompressTool.getInstance();
byte[] buff = new byte[1024];
Random r = new Random();
while (!stop[0]) {
r.nextBytes(buff);
try {
byte[] test = tool.expand(tool.compress(buff, "LZF"));
assertEquals(buff, test);
} catch (Exception e) {
ex[0] = e;
}
}
}
};
threads[i] = t;
t.start();
}
try {
Thread.sleep(1000);
stop[0] = true;
for (Thread t : threads) {
t.join();
}
} catch (InterruptedException e) {
// ignore
}
if (ex[0] != null) {
throw ex[0];
}
}
private void testVariableEnd() throws Exception { private void testVariableEnd() throws Exception {
CompressTool utils = CompressTool.getInstance(); CompressTool utils = CompressTool.getInstance();
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论