我知道像Hashtable这样的集合是同步的,但是有人可以向我解释它是如何工作的,以及在什么时候访问仅限于并发调用?例如,假设我使用了一些像这样的迭代器:
Hashtable<Integer,Integer> map = new Hashtable<Integer,Integer>();
void dosomething1(){
for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
// do something
}
}
void dosomething2(){
for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
// do something
// and remove it
i.remove();
}
}
void putsomething(int a, int b){
map.put(a,b);
}
void removesomething(int a){
map.remove(a);
}
var clear(){
map = new Hashtable<Integer,Integer>();
}
Run Code Online (Sandbox Code Playgroud)
有人可以解释一下我是否有任何陷阱让我从不同的线程中随机调用这些函数?特别是迭代器如何进行同步,特别是在使用entrySet()时,它似乎也需要同步?如果在其中一个循环正在进行时调用clear()会发生什么?如果removedomething()删除了dosomething1()中并发循环尚未处理的项,该怎么办?
谢谢你的帮助!
我是java的新手.我在Threadsafe和synchronized之间有点困惑.线程安全意味着多个线程可以同时使用方法或类实例,而不会出现任何问题.同步意味着只有一个线程可以一次运行.
那么它们如何相互关联?
java multithreading synchronization synchronized thread-safety
多年来,我一直在使用Java Memory Model进行日常工作.我认为我对数据竞争的概念以及避免它们的不同方法(例如,同步块,易变变量等)有很好的理解.但是,仍然有一些我认为我完全不了解内存模型的东西,这是类的最终字段应该是线程安全的方式而没有任何进一步的同步.
所以根据规范,如果一个对象被正确初始化(也就是说,没有引用对象在其构造函数中以某种方式转义,使得引用可以被另一个线程看到),那么,在构造之后,任何看到该对象的线程对象将保证看到对象的所有最终字段的引用(在它们构造时的状态),没有任何进一步的同步.
特别是,标准(http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4)说:
最终字段的使用模型很简单:在该对象的构造函数中设置对象的最终字段; 并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用.如果遵循此原因,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本.它还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的.
他们甚至给出了以下示例:
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
Run Code Online (Sandbox Code Playgroud)
其中线程A应该运行"reader()",并且线程B应该运行"writer()".
到目前为止,显然是如此的好.
我主要担心的是......这在实践中真的有用吗?据我所知,为了使线程A(运行"reader()")看到对"f"的引用,我们必须使用一些同步机制,例如使f volatile,或者使用lock来同步访问F.如果我们不这样做,我们甚至不能保证"reader()"将能够看到初始化的"f",也就是说,由于我们没有同步访问"f",读者可能会看到" null"而不是由writer线程构造的对象.这个问题在http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong中说明,这是Java内存模型的主要参考之一[大胆强调我的]:
现在,已经说过所有这些,如果在一个线程构造一个不可变对象(即一个只包含最终字段的对象)之后,你想确保所有其他线程都能正确看到它,你仍然通常需要使用同步.例如,没有其他方法可以确保第二个线程可以看到对不可变对象的引用.程序从最终字段获得的保证应该仔细调整,仔细了解如何在代码中管理并发.
因此,如果我们甚至不能保证看到对"f"的引用,那么我们必须使用典型的同步机制(volatile,lock等),并且这些机制确实已经导致数据争用消失,最终的需求是我甚至都不会考虑的事情.我的意思是,如果为了使"f"对其他线程可见,我们仍然需要使用volatile或synchronized块,并且它们已经使内部字段对其他线程可见......有什么意义(在线程安全术语中)首先在场上进行决赛?
我正在尝试使用两个线程添加String值ArrayList.我想要的是,当一个线程添加值时,另一个线程不应该干扰,所以我使用了该Collections.synchronizedList方法.但似乎如果我没有在对象上显式同步,则以不同步的方式完成添加.
没有显式同步块:
public class SynTest {
public static void main(String []args){
final List<String> list=new ArrayList<String>();
final List<String> synList=Collections.synchronizedList(list);
final Object o=new Object();
Thread tOne=new Thread(new Runnable(){
@Override
public void run() {
//synchronized(o){
for(int i=0;i<100;i++){
System.out.println(synList.add("add one"+i)+ " one");
}
//}
}
});
Thread tTwo=new Thread(new Runnable(){
@Override
public void run() {
//synchronized(o){
for(int i=0;i<100;i++){
System.out.println(synList.add("add two"+i)+" two");
}
//}
}
});
tOne.start();
tTwo.start();
}
}
Run Code Online (Sandbox Code Playgroud)
我得到的输出是:
true one
true two
true one
true two …Run Code Online (Sandbox Code Playgroud) 我目前的开发项目有两个方面.首先,有一个公共网站,外部用户可以为各种目的提交和更新信息.然后,此信息将保存到colo工具的本地SQL Server.
第二个方面是员工用于管理相同记录(概念上)并提供状态更新,批准等的内部应用程序.此应用程序托管在具有自己的本地SQL Server数据库的公司防火墙内.
这两个网络通过硬件VPN解决方案连接,这是很好的,但显然不是世界上最快的东西.
这两个数据库是相似的,并且共享许多相同的表,但它们不是100%相同.双方的许多表都非常特定于内部或外部应用程序.
所以问题是:当用户更新他们的信息或在公共网站上提交记录时,如何将这些数据传输到内部应用程序的数据库,以便内部员工管理?反之亦然......你如何推动员工的更新回到网站?
值得一提的是,这些更新发生的"实时"越多越好.并不是说它必须是即时的,只是相当快.
到目前为止,我已经考虑过使用以下类型的方法:
有什么建议?有没有人遇到过这个问题?你有没有想出一个适合你的解决方案?
在什么情况下需要同步访问实例成员?我理解,对类的静态成员的访问总是需要同步 - 因为它们在类的所有对象实例之间共享.
我的问题是,如果我不同步实例成员,我什么时候会不正确?
例如,如果我的班级是
public class MyClass {
private int instanceVar = 0;
public setInstanceVar()
{
instanceVar++;
}
public getInstanceVar()
{
return instanceVar;
}
}
Run Code Online (Sandbox Code Playgroud)
在什么情况下(使用类MyClass)我需要有方法:
public synchronized setInstanceVar()和
public synchronized getInstanceVar()?
提前感谢您的回答.
问题如何确保我的应用程序是线程安全的?他们的任何常见做法,测试方法,要避免的事情,要寻找的东西是什么?
背景我目前正在开发一个服务器应用程序,它在不同的线程中执行许多后台任务,并使用Indy与客户端进行通信(使用另一组自动生成的线程进行通信).由于应用程序应该是高度可用的,程序崩溃是一件非常糟糕的事情,我想确保应用程序是线程安全的.无论如何,我不时发现一段代码抛出一个以前从未发生过的异常,在大多数情况下我发现它是某种同步错误,我忘了正确地同步我的对象.因此,我的问题涉及最佳实践,线程安全测试和类似的事情.
mghie:谢谢你的回答!我或许应该更准确一点.为了清楚起见,我了解多线程的原理,我在整个程序中使用同步(监视器),我知道如何将线程问题与其他实现问题区分开来.但尽管如此,我仍然忘记不时添加适当的同步.举个例子,我在代码中使用了RTL排序功能.看起来像
FKeyList.Sort (CompareKeysFunc);
Run Code Online (Sandbox Code Playgroud)
事实证明,我必须在排序时同步FKeyList.在最初编写那么简单的代码行时,我才想起它.这是我想谈的这些问题.一个人容易忘记添加同步代码的地方有哪些?您如何确保在所有重要位置添加同步代码?
我正在编写将并行运行的脚本,并将从同一个文件中获取输入数据.这些脚本将打开输入文件,读取第一行,存储它以供进一步处理,最后从输入文件中擦除此读取行.
现在问题是访问文件的多个脚本可能导致两个脚本同时访问输入文件并读取同一行的情况,这会产生两次处理行的不可接受的结果.
现在一个解决方案是.lock_input在访问输入文件之前编写一个锁文件(),然后在释放输入文件时将其擦除,但是这种解决方案在我的情况下并不吸引人,因为有时NFS会随机减慢网络通信速度并且可能没有可靠的锁定.
另一种解决方案是放置进程锁而不是编写文件,这意味着访问输入文件的第一个脚本将启动一个名为lock_input的进程,其他脚本将启动ps -elf | grep lock_input.如果它出现在进程列表中,它们将等待.这可能比写入NFS更快,但仍然不是完美的解决方案......
所以我的问题是:是否有任何bash命令(或其他脚本解释器)或我可以使用的服务,其行为类似于线程编程中用于同步的信号量或互斥锁?
谢谢.
小粗略的例子:
假设我们有input_file如下:
Monday Tuesday Wednesday Thursday Friday Saturday Sunday
治疗脚本:TrScript.sh
#!/bin/bash
NbLines=$(cat input_file | wc -l)
while [ ! $NbLines = 0 ]
do
FirstLine=$(head -1 input_file)
echo "Hello World today is $FirstLine"
RemainingLines=$(expr $NbLines - 1 )
tail -n $RemainingLines input_file > tmp
mv tmp input_file
NbLines=$(cat input_file | wc -l)
done
Run Code Online (Sandbox Code Playgroud)
主要脚本:
#! /bin/bash
./TrScript.sh &
./TrScript.sh &
./TrScript.sh &
wait
Run Code Online (Sandbox Code Playgroud)
结果应该是:
Hello …
我想在多个线程之间共享一个变量,如下所示:
boolean flag = true;
T1 main = new T1();
T2 help = new T2();
main.start();
help.start();
Run Code Online (Sandbox Code Playgroud)
我想flag在main和help线程之间共享,这些是我创建的两个不同的Java类.有办法做到这一点吗?谢谢!
我在dos.oracle.com找到了这个
public static List synchronizedList(列表列表)
返回由指定列表支持的同步(线程安全)列表.为了保证串行访问,必须通过返回的列表完成对后备列表的所有访问.当迭代它时,用户必须手动同步返回的列表:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:为什么我必须同步列表来迭代它,如果Collections.synchronizedList();它应该返回已经同步的列表?
我只是在两个线程中访问列表:一个线程只添加,另一个线程获取和删除.您建议在此方案中使用哪些其他类?
谢谢阅读.
synchronization ×10
java ×7
collections ×2
arraylist ×1
bash ×1
concurrency ×1
database ×1
delphi ×1
distributed ×1
final ×1
locking ×1
methods ×1
non-static ×1
shared ×1
sql-server ×1
synchronized ×1