Java:同时更改/替换和读取具有2个线程的同一int / Object是否安全?

时间:2018-06-30 15:20:31

标签: java multithreading

例如,有2个线程:一个线程读取int,其他线程更改int值。

这些线程是否不受内存损坏和其他危险因素的侵害?第一个线程会成功地将整数写入变量,而第二个线程会成功地读取变量吗?

对象是否相同?当另一个线程重复读取对象引用时,用另一个对象替换对象引用是否安全?

编辑:第二个线程只会读取它,不会更改变量

2 个答案:

答案 0 :(得分:1)

当一个线程进行写入而另一个线程进行读取时,应该没有问题,但是,如果整数的值很重要,它将产生竞争条件。在这种情况下,请在Java中使用:querySelector和/或<?php include("tf_main.php"); if(isset($_POST['key1'])){ $key = $_POST['key1']; $sql = "SELECT * FROM tf_data WHERE BINARY tfs_key ='".$key."';"; $result = mysqli_query($conn, $sql); $row = mysqli_fetch_assoc($result); //Schlüssel gefunden if (mysqli_num_rows($result)>0 && $row['tfs_aktiv']=='1' && $row['tfs_vname']!=""&& $row['tfs_nname']!=""){ $sql = "SELECT tfs_finderl FROM tf_data WHERE BINARY tfs_key='".$key."';"; $result = mysqli_query($conn, $sql); $row = mysqli_fetch_assoc($result); if($row['tfs_finderl'] == ""){ header("Location: /sie-haben-einen-registrierten-gegenstand-gefunden/?error1=".$error1."&error2=".$error2."&key=".$key."&finderl=0"); }else{ header("Location: /sie-haben-einen-registrierten-gegenstand-gefunden/?error1=".$error1."&error2=".$error2."&key=".$key."&finderl=".$row['tfs_finderl']); } }//Schlüssel ist noch nicht aktiviert / Schlüssel ist ungültig else{ $sql= "select * from tf_data where BINARY tfs_key ='".$key."'"; $result = mysqli_query($conn, $sql); $row = mysqli_fetch_assoc($result); if(mysqli_num_rows($result)>0 && $row['tfs_aktiv']=='1'){ $row = mysqli_fetch_assoc($result); $_SESSION['tfs_key'] = $row['tfs_key']; header("Location: /dieser-traumfinder-ist-noch-nicht-aktiviert/?error4=".$error4."&error1=".$error1."&error2=".$error2."&error3=".$error3."&key=".$key); }else{ header("Location: /dieser-code-ist-nicht-gueltig/"); } } } //Schlüssel aktivieren if(isset($_POST['key_new'])){ //Formular überprüfung $sql = "SELECT * FROM tf_data WHERE BINARY tfs_key='".$_POST['key_new']."';"; $result = mysqli_query($conn, $sql); $row = mysqli_fetch_assoc($result); echo $_POST['key_new']."<br>"; echo mysqli_num_rows($result)."<br>"; echo mysqli_num_rows($result2); echo $row['tfs_vname']; if($row['tfs_vname']!=""){ $error4=1; } if($_POST['vname']==""){ $error1=1; } if($_POST['nname']==""){ $error2=1; } if($_POST['mail']==""){ $error3=1; } //Schreiben in die Datenbank und weiterleitung zur neuen Seite if($error4 != 1 && $error1 != 1 && $error2 != 1 && $error3 != 1){ // $sql = "INSERT INTO tf_data (tfs_key,tfs_vname,tfs_nname,tfs_tel,tfs_mail,tfs_finderl) // VALUES ('".$_POST['key_new']."','".$_POST['vname']."','".$_POST['nname']."','".$_POST['tel']."','".$_POST['mail']."','".$_POST['findl']."');"; $sql= " UPDATE `tf_data` SET `tfs_vname` = '".$_POST['vname']."', `tfs_nname` = '".$_POST['nname']."', `tfs_tel` = '".$_POST['tel']."', `tfs_mail` = '".$_POST['mail']."', `tfs_date` = '".date("d.m.y")."', `tfs_finderl` = '".$_POST['findl']."' WHERE BINARY `tf_data`.`tfs_key` = '".$_POST['key_new']."';"; if(mysqli_query($conn, $sql)){ header("Location: https://www.traumfinder.com/herzlichen-glueckwunsch/"); }else{ echo mysqli_error($conn); } }else{ header("Location: /dieser-traumfinder-ist-noch-nicht-aktiviert/?error4=".$error4."&error1=".$error1."&error2=".$error2."&error3=".$error3."&key=".$_POST['key_new']); } } //Schlüssel wurde gefunden! if(isset($_POST['send_message'])){ $key=$_GET['key']; $sql="SELECT * FROM tf_data WHERE BINARY tfs_key='".$key."';"; $result = mysqli_query($conn, $sql); $row = mysqli_fetch_assoc($result); if($_POST['fname']==""){ $error1 = 1; } if($_POST['fmail']==""){ $error2 = 1; } if($error1 != 1 && $error2 != 1 ){ $empfaenger = $row['tfs_mail']; $betreff = 'Dein registrierter Gegenstand wurde gefunden - Traumfinder'; $nachricht ="Hallo ".$row['tfs_vname']." ".$row['tfs_nname'].",\n\nwir haben eine tolle Neuigkeit für dich: Dein registrierter Gegenstand wurde von ".$_POST['fname']." gefunden!\n\nSo erreichst Du ".$_POST['fname'].":\nE-Mail: ".$_POST['fmail']."\nTel.: ".$_POST['ftel']."\n\nSetze dich am besten sofort mit ihr/ihm in Kontakt.\n\nWICHTIG: Überlege dir bitte genau, welche Daten du preisgibst. Hast du z.B. einen Schlüssel verloren, solltest du nicht die dazu passende Adresse verraten oder mit einer Telefonnummer anrufen, bei der die Adresse im Telefonbuch hinterlegt ist.\n\nWir würden uns auch sehr freuen, wenn Du unseren Service bei Trustpilot bewerten würdest: https://de.trustpilot.com/evaluate/traumfinder.com\n\nDein Traumfinder-Team"; $nachricht= trim($nachricht); // Additional headers $headers = 'From: Traumfinder <admin@wp-staging.de>' . "\r\n"; $headers .= 'Bcc: oliver.meyer@nord-com.net' . "\r\n"; mail($empfaenger, $betreff, $nachricht, $headers); header("Location: /vielen-dank/"); }else{ if($row['tfs_finderl'] == ""){ header("Location: /sie-haben-einen-registrierten-gegenstand-gefunden/?error1=".$error1."&error2=".$error2."&key=".$key."&finderl=0"); }else{ header("Location: /sie-haben-einen-registrierten-gegenstand-gefunden/?error1=".$error1."&error2=".$error2."&key=".$key."&finderl=".$row['tfs_finderl']); } } } ?> 。这些可以防止比赛状况

AtomicInteger

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html

锁定

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html

答案 1 :(得分:1)

是的,从某种意义上讲,您永远不会遇到半更新的int变量(它既可以包含旧值也可以包含新值,但不包含它们的字节),这是安全的。尽管JLS,第17章一点都不有趣,但78910都包含相同的神秘词(这是本文提供的最好的含义)主题,尽管后来暗示Java内存模型可确保比此顺序一致性“少”):

  

在顺序一致的执行中,所有单个动作(例如读取和写入)的总顺序与程序的顺序一致,并且每个单独动作都是原子性的,并且对每个线程都是立即可见的。 / p>

最后有一些“倒置”建议(其中任何一个):

  

17.7。双原子和长原子的非原子处理
  出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两次单独的写入:一次写入每个32位的一半。这可能导致线程在一次写入中看到64位值的前32位,而在另一次写入中看到后32位。

所以这两种64位类型可能不是非原子的,尽管这是首选的情况(它们也是原子的)(此处未引用)。

对于对象引用的特定情况,毫无疑问:

  

对引用的写入和读取始终是原子的,无论它们是实现为32位还是64位值。

建议的AtomicInteger(或其在GitHub上的来源)更像是一个易失的整数,具有内部sun.misc.Unsafe类(大多数xAndY()方法)进行的额外操作。与赤脚实施相比,它具有更高的性能。

顺便说一句:Java中有一个volatile关键字,当您希望在所有平台上看到一个线程中的一个线程发生更新时,应该使用该关键字。

说实话,即使没有volatile,这个奇怪的测试也适用于Windows:

static volatile int c=0;
public static void main(String[] args) {
    Thread t=new Thread(new Runnable() {
        @Override
        public void run() {
            while(!Thread.interrupted())
                c=(c+1)%3;
        }
    });
    t.start();
    int some=10;
    while(some>0)
        if(c==0 && c==1 && c==2){
            System.out.println("Bingo!");
            some--;
        }
    t.interrupt();
}