Java 如何在多线程应用程序中使用区域设置来提高性能
在我的应用程序中,我有一个由多个线程同时调用的方法。每个线程在运行时多次调用此方法Java 如何在多线程应用程序中使用区域设置来提高性能,java,multithreading,Java,Multithreading,在我的应用程序中,我有一个由多个线程同时调用的方法。每个线程在运行时多次调用此方法 private Locale trLoc = new Locale("tr", "TR"); public double calculate(String arg1){ arg1 = arg1.toUpperCase(trLoc); ... } 此方法进行String.toUpperString(Locale)调用,由于Locale类中的HashTable用法,导致瓶颈。当toUpperCase方法运
private Locale trLoc = new Locale("tr", "TR");
public double calculate(String arg1){
arg1 = arg1.toUpperCase(trLoc);
...
}
此方法进行String.toUpperString(Locale)
调用,由于Locale类中的HashTable
用法,导致瓶颈。当toUpperCase
方法运行时,每个线程等待另一个线程。这种情况使我的应用程序速度降低了三倍
在使用区域设置时,我是否缺少一些东西,或者我必须使用另一个类来实现同样的目的
提前感谢。编辑:下面的解决方案实际上不起作用,因为
java.lang.ConditionalSpecialCasing
类中有问题的哈希表是静态的,仍将由所有线程共享。我建议你接受西布尼克的回答,而不是我的
一个简单的解决方案是使trLoc成为一个ThreadLocal
:将为每个线程自动创建一个新实例(根据需要)。如果您有一个线程池或类似的池,这将很好:您将只创建与池中线程数量相同的Locale
实例,这应该是相当合理的。由于每个线程将访问不同的语言环境实例
,因此您将不再有访问同步哈希表的争用
private ThreadLocal<Locale> trLoc = new ThreadLocal<Locale>() {
@Override
protected Locale initialValue() {
return new Locale("tr", "TR");
}
};
public double calculate(String arg1){
arg1 = arg1.toUpperCase(trLoc.get());
...
}
private ThreadLocal trLoc=new ThreadLocal(){
@凌驾
受保护的区域设置初始值(){
返回新的区域设置(“tr”、“tr”);
}
};
公共双计算(字符串arg1){
arg1=arg1.toUpperCase(trLoc.get());
...
}
经过短暂的探索,JDK似乎帮不了你。我建议获取java.lang.ConditionalSpecialCasing
类,复制它并用哈希表修复问题。您可以用HashMap
替换Hashtable
。我看不出有任何理由在这里使用哈希表。基于我运行了一些基准测试的答案
upperCaseEN
使用Local.ENGLISH
upperCaseTR
使用新语言环境(“tr”、“tr”)
JDK 8
jdk8补丁
使用打补丁的条件专用套管
类,使用哈希映射
而不是哈希表
Benchmark Mode Samples Score Score error Units
s.o.MyBenchmark.upperCaseTR thrpt 25 3331.277 77.691 ops/ms
另一种解决方案是,如果字符串包含小写的i
,则首先扫描该字符串。因为这似乎是土耳其语言环境中唯一需要对toUpperCase
进行特殊处理的字符
if (state.lowercase.contains("i")) {
uppercase = lowercase.toUpperCase(TR_LOCALE));
} else {
uppercase = lowercase.toUpperCase(EN_LOCALE));
}
这已经提高了性能
Benchmark Mode Samples Score Score error Units
s.o.MyBenchmark.upperCasePatchedTR thrpt 25 8753.116 51.582 ops/ms
编辑可以在
基于次优的建议,即扫描小写字母“i”,我尝试了一种位代码,将i替换为无害字符,将其替换为大写字母,并将其替换回大写字母。我不可靠的性能测试并没有显示速度有很大的提高,YMMV。请改为使用JMH测试性能,但您已经知道了这一点
然后我尝试了一种基于表格的方法。这和预期的一样快
一如既往,代码仅用于信息。勉强测试过
import java.util.*;
import java.util.concurrent.atomic.*;
public class Benchmark {
// Substitute i Turkish toUpperCase.
public static String toUpperCase(String str) {
int index = str.indexOf('i');
if (index == -1) {
return str.toUpperCase(tr);
} else {
char[] array = str.toCharArray();
char[] localised = toUpperCase(array, index);
return String.valueOf(localised);
}
}
private static char[] toUpperCase(char[] array, int index) {
array[index] = 'X';
int next = indexOf('i', array, index+1);
final char[] localised;
if (next == -1) {
String upper = String.valueOf(array).toUpperCase(tr);
int len = upper.length();
if (len == array.length) {
localised = array;
upper.getChars(0, len, localised, 0);
} else {
localised = upper.toCharArray();
}
} else {
localised = toUpperCase(array, next);
}
localised[index] = '\u0130';
return localised;
}
private static int indexOf(char c, char[] array, int off) {
while (off<array.length && array[off] != c) {
++off;
}
return off==array.length ? -1 : off;
}
// Table-based Turkish toUpperCase.
private static final int limit = 1<<9;
private static final char[] table;
static {
Locale locale = new Locale("tr", "TR");
table = new char[limit];
char[] buff = { ' ' };
for (int c=0; c<limit; ++c) {
buff[0] = (char)c;
String upper = String.valueOf(buff).toUpperCase(locale);
if (upper.length() != 1 && c != 223 && c != 329 && c != 496) { // es zett etc
throw new Error("do not run: "+c);
}
table[c] = upper.charAt(0);
}
}
public static String tableToUpperCase(String str) {
int len = str.length();
char[] buff = new char[len];
int i;
for (i=0; i<len; ++i) {
char c = str.charAt(i);
if (c >= limit || c == 223 || c == 329 || c == 496) {
break;
}
buff[i] = table[c];
}
return i==len ? String.valueOf(buff) : str.toUpperCase(tr);
}
// Ropey benchmark.
private static final Locale tr =
new Locale("tr", "TR");
//Locale.ENGLISH;
public static void main(String[] args) {
System.err.println("friingi".toUpperCase(tr));
System.err.println(toUpperCase("friingi"));
int total = 0;
for (int i=0; i<5; ++i) {
long start = System.nanoTime();
total += run();
long time = System.nanoTime()-start;
System.err.println(time/1000_000_000.0);
}
System.err.println(total);
}
private static int run() {
AtomicInteger total = new AtomicInteger(0);
List<Thread> threads = new ArrayList<>();
for (int i=0; i<100; ++i) {
threads.add(new Thread(() -> {
total.addAndGet(runOne());
}));
}
threads.forEach(Thread::start);
threads.forEach(thread -> {
try {
thread.join();
} catch (Exception exc) {
throw new Error(exc);
}
});
return total.get();
}
private static int runOne() {
int sum = 0;
for (int i=0; i<10_000; ++i) {
sum +=
/**/
tableToUpperCase("fringe")
//toUpperCase("fringe")
/*/
"fringe".toUpperCase(tr)
/**/
.length();
}
return sum;
}
}
import java.util.*;
导入java.util.concurrent.atomic.*;
公共课基准{
//代替我的土耳其图珀案。
公共静态字符串toUpperCase(字符串str){
int index=str.indexOf('i');
如果(索引==-1){
返回str.toUpperCase(tr);
}否则{
char[]数组=str.toCharArray();
char[]localised=toUpperCase(数组,索引);
返回字符串.valueOf(本地化);
}
}
私有静态char[]toUpperCase(char[]数组,int索引){
数组[索引]='X';
int next=indexOf('i',数组,索引+1);
最终字符[]本地化;
如果(下一个==-1){
字符串上限=String.valueOf(数组).toUpperCase(tr);
int len=上限.length();
if(len==array.length){
局部=阵列;
getChars(0,len,localized,0);
}否则{
Localized=upper.toCharArray();
}
}否则{
Localized=toUpperCase(数组,下一个);
}
本地化[索引]='\u0130';
返回本地;
}
私有静态int indexOf(char c,char[]数组,int off){
虽然(关闭此选项不起作用。ConditionalSpecialCasing
仍然在内部使用相同的Hashtable
,无论您创建了多少个Locale
。检查代码后,看起来您肯定是对的-Hashtable是静态的,所有线程都将共享该Hashtable。我已经编辑了我的答案。我很惊讶这样一个“feature”以前没有被检测到。我在谷歌上搜索了与条件特殊情况相关的性能问题,但什么也没有找到。它只发生在(java.lang.String)boolean localeDependent=(lang==“tr”| lang==“az”| lang==“lt”)上
这可能值得报告—我会这样做,但我认为最好是RFE由实际遇到问题的人提交,并且可以提供一个测试用例。如果字符串包含I或ï(可能是其中的大多数),则使用错误的区域设置似乎不正确。(立陶宛人也有类似的问题,我相信还有代孕。)我想你可以把有问题的字母从非特殊的字母中移到大写,然后替换这些字母。@TomHawtin tackline这个问题是关于语言环境tr_tr
的,对于这个问题,只有小写的I
在转换为大写时需要特殊处理。我编辑了我的答案,使之明确。你的答案是d不同的区域设置(包括条件特殊情况)这需要一些修改。(我尝试过用“我不在”。即使在我2014年的MacBook Air(主板从2019年开始…)上有线程,性能也只有一点改进。)@TomHawtin tackline I a
import java.util.*;
import java.util.concurrent.atomic.*;
public class Benchmark {
// Substitute i Turkish toUpperCase.
public static String toUpperCase(String str) {
int index = str.indexOf('i');
if (index == -1) {
return str.toUpperCase(tr);
} else {
char[] array = str.toCharArray();
char[] localised = toUpperCase(array, index);
return String.valueOf(localised);
}
}
private static char[] toUpperCase(char[] array, int index) {
array[index] = 'X';
int next = indexOf('i', array, index+1);
final char[] localised;
if (next == -1) {
String upper = String.valueOf(array).toUpperCase(tr);
int len = upper.length();
if (len == array.length) {
localised = array;
upper.getChars(0, len, localised, 0);
} else {
localised = upper.toCharArray();
}
} else {
localised = toUpperCase(array, next);
}
localised[index] = '\u0130';
return localised;
}
private static int indexOf(char c, char[] array, int off) {
while (off<array.length && array[off] != c) {
++off;
}
return off==array.length ? -1 : off;
}
// Table-based Turkish toUpperCase.
private static final int limit = 1<<9;
private static final char[] table;
static {
Locale locale = new Locale("tr", "TR");
table = new char[limit];
char[] buff = { ' ' };
for (int c=0; c<limit; ++c) {
buff[0] = (char)c;
String upper = String.valueOf(buff).toUpperCase(locale);
if (upper.length() != 1 && c != 223 && c != 329 && c != 496) { // es zett etc
throw new Error("do not run: "+c);
}
table[c] = upper.charAt(0);
}
}
public static String tableToUpperCase(String str) {
int len = str.length();
char[] buff = new char[len];
int i;
for (i=0; i<len; ++i) {
char c = str.charAt(i);
if (c >= limit || c == 223 || c == 329 || c == 496) {
break;
}
buff[i] = table[c];
}
return i==len ? String.valueOf(buff) : str.toUpperCase(tr);
}
// Ropey benchmark.
private static final Locale tr =
new Locale("tr", "TR");
//Locale.ENGLISH;
public static void main(String[] args) {
System.err.println("friingi".toUpperCase(tr));
System.err.println(toUpperCase("friingi"));
int total = 0;
for (int i=0; i<5; ++i) {
long start = System.nanoTime();
total += run();
long time = System.nanoTime()-start;
System.err.println(time/1000_000_000.0);
}
System.err.println(total);
}
private static int run() {
AtomicInteger total = new AtomicInteger(0);
List<Thread> threads = new ArrayList<>();
for (int i=0; i<100; ++i) {
threads.add(new Thread(() -> {
total.addAndGet(runOne());
}));
}
threads.forEach(Thread::start);
threads.forEach(thread -> {
try {
thread.join();
} catch (Exception exc) {
throw new Error(exc);
}
});
return total.get();
}
private static int runOne() {
int sum = 0;
for (int i=0; i<10_000; ++i) {
sum +=
/**/
tableToUpperCase("fringe")
//toUpperCase("fringe")
/*/
"fringe".toUpperCase(tr)
/**/
.length();
}
return sum;
}
}