อะไรคือสาเหตุที่ไม่อนุญาตให้ "ซิงโครไนซ์" ในวิธีส่วนต่อประสาน Java 8?


210

ใน Java 8 ฉันสามารถเขียนได้อย่างง่ายดาย:

interface Interface1 {
    default void method1() {
        synchronized (this) {
            // Something
        }
    }

    static void method2() {
        synchronized (Interface1.class) {
            // Something
        }
    }
}

ฉันจะได้รับความหมายของการซิงโครไนซ์เต็มรูปแบบที่ฉันสามารถใช้ได้ในชั้นเรียน อย่างไรก็ตามฉันไม่สามารถใช้โมดิsynchronizedฟายเออร์ในการประกาศเมธอด:

interface Interface2 {
    default synchronized void method1() {
        //  ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
    }

    static synchronized void method2() {
        // ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
    }
}

ในตอนนี้เราสามารถยืนยันได้ว่าทั้งสองอินเตอร์เฟสนั้นทำงานในลักษณะเดียวกันยกเว้นว่าจะInterface2สร้างสัญญาขึ้นmethod1()และต่อmethod2()ไปซึ่งค่อนข้างแข็งแกร่งกว่าสิ่งที่Interface1ทำ แน่นอนเราอาจโต้แย้งว่าdefaultการใช้งานไม่ควรตั้งสมมติฐานใด ๆ เกี่ยวกับสถานะการนำไปปฏิบัติที่เป็นรูปธรรมหรือว่าคำหลักดังกล่าวไม่สามารถดึงน้ำหนักได้

คำถาม:

อะไรคือสาเหตุที่กลุ่มผู้เชี่ยวชาญ JSR-335 ตัดสินใจไม่สนับสนุนsynchronizedวิธีการเชื่อมต่อ


1
การซิงโครไนซ์เป็นพฤติกรรมการใช้งานและจะเปลี่ยนผลลัพธ์รหัสไบต์สุดท้ายที่ทำโดยคอมไพเลอร์เพื่อให้สามารถใช้ข้างรหัส มันไม่มีเหตุผลในการประกาศวิธีการ มันควรจะสับสนว่าคอมไพเลอร์สร้างอะไรขึ้นมาบ้าง
Martin Strejc

@MartinStrejc: นั่นอาจเป็นคำอธิบายสำหรับการละเว้นdefault synchronizedแต่ก็ไม่จำเป็นสำหรับstatic synchronizedแม้ว่าฉันจะยอมรับว่าหลังนั้นอาจถูกละเว้นด้วยเหตุผลที่สอดคล้องกัน
Lukas Eder

1
ฉันไม่แน่ใจว่าคำถามนี้จะเพิ่มค่าใด ๆ เนื่องจากโมดิsynchronizedฟายเออร์อาจจะ overriden ในคลาสย่อยดังนั้นมันจะสำคัญถ้ามีบางอย่างที่เป็นวิธีการเริ่มต้นขั้นสุดท้าย (คำถามอื่น ๆ ของคุณ)
skiwi

@skiwi: อาร์กิวเมนต์ที่เอาชนะนั้นไม่เพียงพอ คลาสย่อยอาจแทนที่เมธอดที่ประกาศไว้synchronizedในคลาสซุปเปอร์ ฉันจะไม่แปลกใจเลยที่การไม่สนับสนุนsynchronizedและไม่สนับสนุนfinalนั้นอาจเกี่ยวข้องกับมรดกหลายอย่าง (เช่นการสืบทอดvoid x() และ synchronized void x()อื่น ๆ ) แต่นั่นคือการเก็งกำไร ฉันอยากรู้เกี่ยวกับเหตุผลที่เชื่อถือได้หากมี
Lukas Eder

2
>> "subclasses อาจแทนที่วิธีที่ถูกประกาศให้ตรงกันใน super class ได้อย่างมีประสิทธิภาพลบการประสาน" ... เฉพาะในกรณีที่พวกเขาไม่เรียกsuperซึ่งต้องมีการใช้งานเต็มรูปแบบและการเข้าถึงที่เป็นไปได้ให้กับสมาชิกส่วนตัว Btw มีเหตุผลที่วิธีการเหล่านั้นเรียกว่า "ผู้พิทักษ์" - พวกเขานำเสนอเพื่อให้ง่ายต่อการเพิ่มวิธีการใหม่
bestsss

คำตอบ:


260

ในขณะที่ในตอนแรกอาจดูเหมือนชัดเจนว่าใครต้องการสนับสนุนการsynchronizedแก้ไขในวิธีการเริ่มต้นปรากฎว่าการทำเช่นนั้นจะเป็นอันตรายและเป็นสิ่งต้องห้าม

วิธีการซิงโครไนซ์เป็นชวเลขสำหรับวิธีการที่ทำงานราวกับว่าร่างกายทั้งหมดถูกล้อมรอบในsynchronizedบล็อกที่มีวัตถุล็อคเป็นผู้รับ มันอาจดูสมเหตุสมผลที่จะขยายความหมายนี้ไปยังวิธีการเริ่มต้นเช่นกัน ท้ายที่สุดพวกเขาเป็นวิธีการที่มีผู้รับเช่นกัน (โปรดทราบว่าsynchronizedวิธีการทั้งหมดเป็นการเพิ่มประสิทธิภาพของวากยสัมพันธ์โดยไม่จำเป็นต้องมีขนาดกะทัดรัดกว่าsynchronizedบล็อกที่เกี่ยวข้องมีอาร์กิวเมนต์ที่สมเหตุสมผลว่านี่คือการเพิ่มประสิทธิภาพวากยสัมพันธ์ก่อนวัยอันควรในสถานที่แรกและวิธีการที่ตรงกัน ทำให้เกิดปัญหามากกว่าที่พวกเขาแก้ แต่เรือลำนั้นแล่นไปนานแล้ว)

แล้วทำไมพวกเขาถึงอันตราย การซิงโครไนซ์เป็นเรื่องเกี่ยวกับการล็อค การล็อคเป็นเรื่องเกี่ยวกับการประสานการเข้าถึงที่แบ่งปันไปยังสถานะที่ไม่แน่นอน แต่ละวัตถุควรมีนโยบายการซิงโครไนซ์ที่กำหนดว่าล็อกตัวป้องกันใดซึ่งตัวแปรสถานะ (ดูJava Concurrency ในทางปฏิบัติ , ส่วนที่ 2.4)

วัตถุจำนวนมากใช้เป็นนโยบายการซิงโครไนซ์รูปแบบการตรวจสอบ Java (JCiP 4.1) ซึ่งสถานะของวัตถุนั้นได้รับการปกป้องโดยการล็อคที่แท้จริง ไม่มีอะไรวิเศษหรือพิเศษเกี่ยวกับรูปแบบนี้ แต่สะดวกและการใช้synchronizedคำหลักในวิธีการโดยนัยถือว่ารูปแบบนี้

เป็นคลาสที่เป็นเจ้าของสถานะที่ได้รับการกำหนดนโยบายการซิงโครไนซ์ของวัตถุนั้น แต่อินเทอร์เฟซไม่ได้เป็นเจ้าของสถานะของวัตถุที่ผสมกันดังนั้นการใช้วิธีการซิงโครไนซ์ในอินเทอร์เฟซจะถือว่าเป็นนโยบายการซิงโครไนซ์โดยเฉพาะ แต่สิ่งที่คุณไม่มีพื้นฐานที่สมเหตุสมผลสำหรับการสมมติ การใช้งานการซิงโครไนซ์นั้นไม่มีความปลอดภัยเพิ่มเติมของเธรดใด ๆ (คุณอาจซิงโครไนซ์กับการล็อคผิด) นี่จะทำให้คุณรู้สึกมั่นใจอย่างผิด ๆ ว่าคุณได้ทำอะไรบางอย่างเกี่ยวกับความปลอดภัยของเธรดและไม่มีข้อความแสดงข้อผิดพลาดที่บอกคุณว่าคุณกำลังสมมติว่านโยบายการซิงโครไนซ์ผิด

มันยากพอที่จะรักษานโยบายการซิงโครไนซ์ไฟล์ต้นฉบับอย่างสม่ำเสมอ มันยิ่งยากกว่านี้เพื่อให้แน่ใจว่าคลาสย่อยถูกต้องตามนโยบายการซิงโครไนซ์ที่กำหนดโดยซูเปอร์คลาส การพยายามทำระหว่างคลาสที่มีการคับ (เช่นอินเทอร์เฟซและคลาสที่อาจใช้งานได้) นั้นแทบจะเป็นไปไม่ได้และเกิดข้อผิดพลาดได้ง่าย

เมื่อพิจารณาข้อโต้แย้งเหล่านั้นทั้งหมดแล้วอะไรจะเป็นข้อโต้แย้ง ดูเหมือนว่าพวกเขาส่วนใหญ่เกี่ยวกับการทำให้อินเทอร์เฟซทำงานเหมือนลักษณะ แม้ว่านี่จะเป็นความต้องการที่เข้าใจได้ แต่ศูนย์ออกแบบสำหรับวิธีการเริ่มต้นคือวิวัฒนาการของส่วนต่อประสานไม่ใช่ "ลักษณะ -" ในกรณีที่ทั้งสองประสบความสำเร็จอย่างต่อเนื่องเรามุ่งมั่นที่จะทำเช่นนั้น แต่เมื่อใดที่หนึ่งขัดแย้งกับอีกเราต้องเลือกตามเป้าหมายการออกแบบหลัก


26
โปรดทราบว่าใน JDK 1.1 synchronizedตัวดัดแปลงวิธีการนั้นปรากฏในเอาต์พุต javadoc ทำให้ผู้คนเข้าใจผิดว่าเป็นส่วนหนึ่งของข้อกำหนด ปัญหานี้ได้รับการแก้ไขใน JDK 1.2 แม้ว่ามันจะปรากฏบนวิธีการสาธารณะsynchronizedแก้ไขเป็นส่วนหนึ่งของการใช้งานไม่ใช่สัญญา (เหตุผลและการรักษาที่คล้ายกันเกิดขึ้นสำหรับnativeตัวดัดแปลง)
Stuart Marks

15
ข้อผิดพลาดทั่วไปในโปรแกรม Java ยุคแรกคือโรยพอsynchronizedและทำเธรดส่วนประกอบที่ปลอดภัยรอบ ๆ และคุณมีโปรแกรมเซฟเธรดเกือบ ปัญหาคือสิ่งนี้มักจะใช้งานได้ แต่มันก็แตกในรูปแบบที่น่าประหลาดใจและเปราะบาง ฉันยอมรับว่าการเข้าใจว่าการล็อกของคุณทำงานอย่างไรเป็นกุญแจสำคัญสำหรับแอปพลิเคชันที่มีประสิทธิภาพ
Peter Lawrey

10
@BrianGoetz เหตุผลที่ดีมาก แต่ทำไมถึงsynchronized(this) {...}ได้รับอนุญาตในdefaultวิธีการ? (ดังที่แสดงในคำถามของ Lukas) นั่นไม่อนุญาตให้วิธีการเริ่มต้นเป็นเจ้าของสถานะของคลาสการใช้งานหรือไม่ เราไม่ต้องการป้องกันสิ่งนั้นด้วยหรือ เราจะต้องใช้กฎ FindBugs เพื่อค้นหาเคสที่นักพัฒนาที่ไม่มีข้อมูลทำเช่นนั้นหรือไม่?
Geoffrey De Smet

17
@Geoffrey: ไม่ไม่มีเหตุผลที่จะ จำกัด สิ่งนี้ (แม้ว่ามันควรจะใช้ด้วยความระมัดระวังเสมอ) บล็อกการซิงค์ต้องการให้ผู้เขียนเลือกวัตถุล็อคอย่างชัดเจน สิ่งนี้ช่วยให้พวกเขามีส่วนร่วมในนโยบายการซิงโครไนซ์ของวัตถุอื่นถ้าพวกเขารู้ว่านโยบายนั้นคืออะไร ส่วนที่อันตรายคือสมมติว่าการซิงโครไนซ์กับ 'this' (ซึ่งเป็นวิธีการซิงค์ที่ทำ) มีความหมายจริง สิ่งนี้จะต้องเป็นการตัดสินใจที่ชัดเจนยิ่งขึ้น ที่กล่าวว่าฉันคาดว่าบล็อกซิงค์ในวิธีการอินเตอร์เฟซจะค่อนข้างหายาก
Brian Goetz

6
@GeoffreyDeSmet: synchronized(vector)ด้วยเหตุผลเดียวกันคุณสามารถทำเช่น คุณต้องการความปลอดภัยคุณไม่ควรใช้วัตถุสาธารณะ (เช่นthisตัวเอง) เพื่อล็อค
Yogu

0
public class ParentSync {

public synchronized void parentStart() {
    System.out.println("I am " + this.getClass() + " . parentStarting. now:" + nowStr());
    try {
        Thread.sleep(30000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("I am " + this.getClass() + " . parentFinished. now" + nowStr());
}

private String nowStr() {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
}


public class SonSync1 extends ParentSync {
public void sonStart() {
    System.out.println("I am " + this.getClass() + ". sonStarting,calling parent now ... ");
    super.parentStart();
    System.out.println("I am " + this.getClass() + ". sonFinished");
}
}



public class SonSync2 extends ParentSync {

public void sonStart() {
    System.out.println("I am " + this.getClass() + ". sonStarting,calling parent now ... ");
    super.parentStart();
    System.out.println("I am " + this.getClass() + ". sonFinished");
}
}



public class SyncTest {
public static void main(String[] args) throws Exception {

    new Thread(() -> {
        new SonSync1().sonStart();
    }).start();

    new Thread(() -> {
        new SonSync2().sonStart();
    }).start();

    System.in.read();
}
}

ผลลัพธ์:

I am class com.common.interface18_design.whynotsync_onmethod.SonSync1. sonStarting,calling parent now ... 
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2. sonStarting,calling parent now ... 
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2 . parentStarting. now:2019-04-18 09:50:08
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1 . parentStarting. now:2019-04-18 09:50:08
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1 . parentFinished. now2019-04-18 09:50:38
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1. sonFinished
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2 . parentFinished. now2019-04-18 09:50:38
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2. sonFinished

(ขออภัยที่ใช้คลาสแม่เป็นตัวอย่าง)

จากผลที่ได้เราจะรู้ได้ว่าการล็อคคลาสพาเรนต์นั้นเป็นของทุกคลาสย่อยวัตถุ SonSync1 และ SonSync2 มีการล็อควัตถุที่แตกต่างกัน ล็อคทุกคนมีความเป็นอิสระ ดังนั้นในกรณีนี้ฉันคิดว่าไม่เป็นอันตรายหากใช้การซิงโครไนซ์ในคลาสผู้ปกครองหรืออินเทอร์เฟซทั่วไป ใครสามารถอธิบายเพิ่มเติมเกี่ยวกับเรื่องนี้?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.