ฉันควรใช้ตัวแปร ThreadLocal เมื่อใดและอย่างไร


876

ฉันควรใช้ThreadLocalตัวแปรเมื่อใด

มันใช้งานอย่างไร?


11
หากคุณใช้ ThreadLocal อย่าเขียนเสื้อคลุมทับเลย !!! นักพัฒนาแต่ละคนและทุกคนที่ต้องการใช้ตัวแปรจะต้องรู้ว่ามันคือ 'ThreadLocal'
ไม่ใช่ข้อผิดพลาด

2
@Notabug หากคุณมีความชัดเจนคุณสามารถตั้งชื่อตัวแปรของคุณเพื่อที่จะว่าคุณกำลังจัดการกับค่า ThreadLocal RequestUtils.getCurrentRequestThreadLocal()เช่น แม้ว่าจะไม่ได้กล่าวถึงสิ่งนี้ แต่ก็มีสาเหตุมาจากความจริงที่ว่า ThreadLocal นั้นไม่ได้สวยงามมากในกรณีส่วนใหญ่
whirlwin

@Sasha Can สามารถใช้ ThreadLocal เพื่อจัดเก็บการติดตามการเรียกใช้งานได้
gaurav

คำตอบ:


864

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

ตัวอย่างเช่น:

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

เอกสารประกอบ


160
ทางเลือกอื่นในการซิงโครไนซ์หรือ threadlocal คือการทำให้ตัวแปรเป็นตัวแปรโลคัล vars เฉพาะที่ปลอดภัยเสมอ ฉันคิดว่ามันเป็นแนวปฏิบัติที่ไม่ดีที่จะทำให้ DateFormats อยู่ในพื้นที่เพราะราคาแพงในการสร้าง แต่ฉันไม่เคยเห็นตัวชี้วัดที่ชัดเจนในหัวข้อนี้
Julien Chastang

18
นี่เป็นราคาที่สูงสำหรับการแฮ็SimpleDateFormatค บางทีมันอาจจะดีกว่าที่จะใช้เป็นทางเลือกด้ายปลอดภัย หากคุณยอมรับว่าซิงเกิลตันนั้นThreadLocalแย่ยิ่งกว่านั้นอีก
Alexander Ryzhov

4
@overthink มีเหตุผลที่จะประกาศ ThreadLocal คงที่และสุดท้ายฉันหมายถึงประสิทธิภาพหรืออะไร?
Sachin Gorade

4
กระบวนการ ThreadLocal.get () วิธีการจะเรียก ThreadLocal.initialValue () (ครั้งเดียว) สำหรับแต่ละเธรดซึ่งหมายความว่าวัตถุ SimpleDateFormat ถูกสร้างขึ้นสำหรับแต่ละหัวข้อ มันจะดีกว่าหรือไม่ที่จะให้ SimpleDateFormat เป็นตัวแปรในตัวเครื่อง (เนื่องจากเราไม่ต้องจัดการกับปัญหาการรวบรวมขยะ)
sudeepdino008

3
เพียงระวังอย่าใช้การเริ่มต้นวงเล็บปีกกาคู่สำหรับ SimpleDateFormat ของคุณเพราะมันจะสร้างคลาสที่ไม่ระบุชื่อเพื่อให้คลาสโหลดเดอร์ของคุณไม่สามารถรวบรวมขยะได้ หน่วยความจำรั่ว: ส่งคืน SimpleDateFormat () ใหม่ {{ApplyPattern ("yyyyMMdd HHmm")}} ใหม่;
user1944408

427

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

หากคุณไม่ได้ทำความสะอาดเมื่อเสร็จสิ้นการอ้างอิงใด ๆ ที่เก็บไว้ในคลาสที่โหลดเนื่องจากส่วนหนึ่งของ webapp ที่ใช้งานจะยังคงอยู่ในฮีปถาวรและจะไม่ได้รับการรวบรวมขยะ การปรับใช้ / ยกเลิกการปรับใช้ webapp จะไม่ล้างข้อมูลThreadอ้างอิงของคลาส webapp ของคุณเนื่องจากThreadไม่ได้เป็นของ webapp ของคุณ การปรับใช้อย่างต่อเนื่องแต่ละครั้งจะสร้างอินสแตนซ์ใหม่ของคลาสซึ่งจะไม่ถูกรวบรวมขยะ

คุณจะจบลงด้วยข้อยกเว้นหน่วยความจำไม่เพียงพอเนื่องจากjava.lang.OutOfMemoryError: PermGen spaceและหลังจาก googling บางอย่างอาจจะเพิ่มขึ้น-XX:MaxPermSizeแทนที่จะแก้ไขข้อผิดพลาด

หากคุณไม่สิ้นสุดประสบปัญหาเหล่านี้คุณสามารถกำหนดหัวข้อและชั้นจะรักษาอ้างอิงเหล่านี้โดยใช้การวิเคราะห์หน่วยความจำของ Eclipseและ / หรือโดยทำตามคำแนะนำของแฟรงก์ Kieviet ของและการติดตามผล

อัปเดต: ค้นพบบล็อกของ Alex Vasseur อีกครั้งซึ่งช่วยให้ฉันติดตามThreadLocalปัญหาบางอย่างที่ฉันมี


11
Alex Vasseur ย้ายบล็อกของเขา นี่คือลิงค์ปัจจุบันไปยังบทความหน่วยความจำรั่ว
Kenster

12
ดูเหมือนว่ากระทู้ของจูเลียนจะย้ายไปที่นี่และมันก็คุ้มค่าที่จะอ่าน…
Donal Fellows

28
นี่เป็นจำนวนมากของ upvotes อันยิ่งใหญ่สำหรับคำตอบที่ในขณะที่ข้อมูลไม่ได้ตอบคำถามจริงๆ
Robin

8
ตอนนี้ PermGen นั้นถูกฆ่าโดย Java 8 แล้วคำตอบนี้จะเปลี่ยนไปหรือเปล่า?
เครื่องซักผ้า Evil

13
@ Robin: ฉันไม่เห็นด้วย คำถามเกี่ยวกับวิธีการใช้ ThreadLocal อย่างถูกต้องเพื่อทำความเข้าใจแนวคิดใด ๆ (ThreadLocal ที่นี่) สิ่งสำคัญคือต้องเข้าใจว่าจะไม่ใช้มันอย่างไรและความเสี่ยงในการใช้อย่างไม่ระมัดระวังและนั่นคือคำตอบของฟิล ฉันดีใจที่เขาพูดถึงประเด็นที่ไม่ได้กล่าวถึงในคำตอบอื่น ๆ คะแนนทั้งหมดเหล่านั้นสมควรได้รับอย่างดี ฉันเชื่อว่า SO ควรสร้างความเข้าใจในแนวคิดมากกว่าแค่เป็นเว็บไซต์ QA
Saurabh Patil

166

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

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


4
นั่นคือวิธีที่เฟรมเวิร์ก exPOJO (www.expojo.com) อนุญาตให้เข้าถึง ORM Session / PersistenceManager โดยไม่จำเป็นต้องมีค่าใช้จ่ายในการเพิ่มความคิดเห็นและการฉีด มันเหมือนกับ 'การฉีดด้าย' แทนที่จะเป็น 'การฉีดวัตถุ' มันให้การเข้าถึงการพึ่งพาโดยไม่ต้องมีความต้องการ (และค่าใช้จ่าย) ของพวกเขาฝังอยู่ในทุกวัตถุที่อาจต้องพึ่งพาเหล่านั้น มันยอดเยี่ยมมากที่ 'น้ำหนักเบา' คุณสามารถสร้าง DI Framework เมื่อคุณใช้การฉีดเกลียวแทน DI แบบคลาสสิค (เช่นสปริง ฯลฯ ฯลฯ )
Volksman

27
ทำไมฉันถึงต้องเลื่อนลงเพื่อค้นหาคำตอบที่สำคัญนี้!
Jeremy Stein

1
@Esko ในโครงการของคุณการใช้ ThreadLocal ใน hashcode และเท่ากับนั้นไม่จำเป็น วิธีที่ดีกว่าคือการสร้างหรือส่งการอ้างอิงไปยังฟังก์ชัน hashcode / equals ทุกครั้งที่จำเป็น ด้วยวิธีนี้คุณสามารถหลีกเลี่ยงการแฮ็ค ThreadLocal ได้อย่างสมบูรณ์
Pacerier

1
@JeremyStein คำตอบที่ดีกว่าอยู่ที่stackoverflow.com/a/817911/632951และstackoverflow.com/a/17398338/632951และstackoverflow.com/a/818364/632951
Pacerier

1
นี่เป็นคำอธิบายที่ดีที่สุดในการใช้ TL
เด็กซ์เตอร์

52

ใน Java ถ้าคุณมี datum ที่สามารถเปลี่ยนแปลงได้ต่อเธรดตัวเลือกของคุณจะต้องส่ง datum นั้นไปยังทุกวิธีที่ต้องการ (หรืออาจต้องการ) หรือเชื่อมโยง datum กับ thread การส่งผ่านข้อมูลรอบตัวทุกที่อาจใช้การได้หากวิธีการทั้งหมดของคุณต้องผ่านตัวแปร "บริบท" ทั่วไปแล้ว

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


13
ดังนั้นคุณควรหลีกเลี่ยงเธรดในลักษณะเดียวกับที่คุณหลีกเลี่ยง globals ฉันไม่สามารถยอมรับได้ว่ามันโอเคที่จะสร้าง globals (thread locals) แทนที่จะส่งค่าไปรอบ ๆ คนไม่ชอบเพราะมักจะเปิดเผยปัญหาสถาปัตยกรรมที่พวกเขาไม่ต้องการแก้ไข
Juan Mendes

10
บางที ... มันอาจมีประโยชน์ แต่ถ้าคุณมี codebase ขนาดใหญ่ที่มีอยู่ซึ่งคุณต้องเพิ่ม datum ใหม่ที่ต้องส่งไปทุกหนทุกแห่งเช่นบริบทเซสชันธุรกรรม db ผู้ใช้ที่ล็อกอินเป็นต้น .
Cornel Masson

6
ความรุ่งโรจน์สำหรับการใช้ข้อมูลแทนที่จะเป็นข้อมูล
ลึก

นี่ทำให้ฉันอยากรู้ 'datum' is the singular form and 'data' is the plural form.
สิทธาธา

23

มีตัวอย่างที่ดีมากในหนังสือคือJava Concurrency ในการปฏิบัติ โดยที่ผู้แต่ง ( Joshua Bloch ) อธิบายว่าการ จำกัด เธรดเป็นหนึ่งในวิธีที่ง่ายที่สุดในการบรรลุความปลอดภัยของเธรดและThreadLocalเป็นวิธีการที่เป็นทางการมากขึ้นในการคงการเก็บเธรดไว้ ในที่สุดเขาก็อธิบายว่าผู้คนสามารถใช้วิธีการที่ไม่เหมาะสมได้อย่างไรโดยใช้มันเป็นตัวแปรระดับโลก

ฉันได้คัดลอกข้อความจากหนังสือที่กล่าวถึงแล้ว แต่รหัส 3.10 ขาดหายไปเนื่องจากไม่สำคัญที่จะเข้าใจว่าควรใช้ ThreadLocal ใด

ตัวแปรเธรดโลคัลมักถูกใช้เพื่อป้องกันการแบ่งใช้ในการออกแบบโดยยึดตาม Singletons ที่ไม่แน่นอนหรือตัวแปรโกลบอล ตัวอย่างเช่นแอปพลิเคชันแบบเธรดเดียวอาจรักษาการเชื่อมต่อฐานข้อมูลส่วนกลางที่เริ่มต้นเมื่อเริ่มต้นเพื่อหลีกเลี่ยงการผ่านการเชื่อมต่อกับทุกวิธี เนื่องจากการเชื่อมต่อ JDBC อาจไม่ปลอดภัยสำหรับเธรดแอ็พพลิเคชันแบบมัลติเธรดที่ใช้การเชื่อมต่อแบบโกลบอลโดยไม่มีการประสานงานเพิ่มเติมจึงไม่ได้รับการตอบกลับอย่างปลอดภัยเช่นกัน โดยใช้ ThreadLocal เพื่อจัดเก็บการเชื่อมต่อ JDBC เช่นเดียวกับใน ConnectionHolder ในรายการ 3.10 แต่ละเธรดจะมีการเชื่อมต่อของตัวเอง

ThreadLocal ใช้กันอย่างแพร่หลายในการนำเฟรมเวิร์กแอปพลิเคชันไปใช้ ตัวอย่างเช่นคอนเทนเนอร์ J2EE เชื่อมโยงบริบทการทำธุรกรรมกับเธรดที่กำลังดำเนินการสำหรับช่วงเวลาของการโทร EJB สิ่งนี้ถูกนำไปใช้อย่างง่ายดายโดยใช้ Thread-Local แบบคงที่ซึ่งถือบริบทการทำธุรกรรม: เมื่อรหัสเฟรมเวิร์กต้องการตรวจสอบว่าธุรกรรมใดที่กำลังทำงานอยู่จะดึงบริบทของธุรกรรมจาก ThreadLocal นี้ สิ่งนี้สะดวกในการลดความจำเป็นในการส่งผ่านข้อมูลบริบทการดำเนินการไปยังทุกวิธี แต่รับรหัสใด ๆ ที่ใช้กลไกนี้ไปยังกรอบงาน

เป็นการง่ายที่จะละเมิด ThreadLocal โดยการใช้คุณสมบัติการเก็บรักษาเธรดในฐานะสิทธิ์การใช้งานเพื่อใช้ตัวแปรทั่วโลกหรือเป็นวิธีการสร้างอาร์กิวเมนต์เมธอด“ ซ่อน” เช่นเดียวกับตัวแปรทั่วโลกตัวแปรของเธรดโลคัลสามารถเบี่ยงเบนจากการนำมาใช้ซ้ำและแนะนำข้อต่อที่ซ่อนอยู่ในคลาสดังนั้นจึงควรใช้ด้วยความระมัดระวัง


18

โดยพื้นฐานแล้วเมื่อคุณต้องการค่าของตัวแปรขึ้นอยู่กับเธรดปัจจุบันและไม่สะดวกที่คุณจะแนบค่ากับเธรดในวิธีอื่น (ตัวอย่างเช่นเธรดคลาสย่อย)

กรณีทั่วไปคือที่เฟรมเวิร์กอื่นสร้างเธรดที่โค้ดของคุณกำลังทำงานอยู่เช่นคอนเทนเนอร์ servlet หรือที่มันเหมาะสมกว่าที่จะใช้ ThreadLocal เนื่องจากตัวแปรของคุณนั้น "อยู่ในตำแหน่งตรรกะ" (แทนที่จะเป็นตัวแปร ห้อยจากคลาสย่อยของเธรดหรือในแผนที่แฮชอื่น ๆ )

บนเว็บไซต์ของฉันฉันมีการสนทนาและตัวอย่างเพิ่มเติมเกี่ยวกับการใช้ ThreadLocalที่อาจเป็นที่สนใจ

บางคนสนับสนุนการใช้ ThreadLocal เป็นวิธีการแนบ "thread ID" กับแต่ละเธรดในอัลกอริทึมที่เกิดขึ้นพร้อมกันที่คุณต้องการหมายเลขเธรด (ดูเช่น Herlihy & Shavit) ในกรณีเช่นนี้ให้ตรวจสอบว่าคุณได้รับประโยชน์จริงๆ!


สามารถใช้ ThreadLocal เพื่อจัดเก็บการติดตามการประมวลผลได้
gaurav

13
  1. ThreadLocal ใน Java ได้รับการแนะนำให้รู้จักกับ JDK 1.2 แต่ต่อมาถูกสร้างใน JDK 1.5 เพื่อแนะนำความปลอดภัยของประเภทบนตัวแปรของ ThreadLocal

  2. ThreadLocal สามารถเชื่อมโยงกับขอบเขตของเธรดรหัสทั้งหมดที่ดำเนินการโดยเธรดมีการเข้าถึงตัวแปร ThreadLocal แต่สองเธรดไม่สามารถมองเห็นตัวแปร ThreadLocal ซึ่งกันและกันได้

  3. แต่ละเธรดมีสำเนาพิเศษของตัวแปร ThreadLocal ซึ่งมีสิทธิ์ในการรวบรวมขยะหลังจากที่เธรดเสร็จสิ้นหรือเสียชีวิตตามปกติหรือเนื่องมาจากการยกเว้นใด ๆ เนื่องจากตัวแปร ThreadLocal เหล่านั้นไม่มีการอ้างอิงสดอื่น ๆ

  4. ตัวแปร ThreadLocal ใน Java โดยทั่วไปเป็นเขตข้อมูลคงที่ส่วนตัวในชั้นเรียนและรักษาสถานะของมันในหัวข้อ

อ่านเพิ่มเติม: ThreadLocal ใน Java - ตัวอย่างโปรแกรมและบทช่วยสอน


สามารถใช้ ThreadLocal เพื่อจัดเก็บการติดตามการประมวลผลได้
gaurav

11

เอกสารบอกว่ามันดีมาก: "แต่ละเธรดที่เข้าถึง [ตัวแปรเธรดโลคัล] (ผ่านเมธอด get หรือ set) มีสำเนาของตัวแปรที่เริ่มต้นได้อย่างอิสระ"

คุณใช้หนึ่งเธรดเมื่อแต่ละเธรดต้องมีสำเนาของตัวเอง โดยค่าเริ่มต้นข้อมูลจะถูกแชร์ระหว่างเธรด


18
โดยค่าเริ่มต้นวัตถุคงที่หรือวัตถุที่ส่งผ่านอย่างชัดเจนระหว่างเธรดที่เธรดทั้งสองมีการอ้างอิงถึงวัตถุเดียวกันจะถูกแชร์ วัตถุที่ประกาศแบบโลคัลในเธรดไม่ถูกแชร์ (เป็นแบบโลคัลกับสแต็กของเธรด) แค่ต้องการชี้แจงว่า
Ian Varley

@IanVarley: ขอบคุณที่ชี้ให้เห็น ดังนั้นถ้าเรามีตัวแปรที่ประกาศและใช้ในคลาส MyThread เราจำเป็นต้องใช้ ThreadLocal ในกรณีนั้นหรือไม่? ตัวอย่าง: คลาส MyThread ขยายเธรด {String var1 ;, int var2; } ในกรณีนี้ var1 และ var2 จะเป็นส่วนหนึ่งของสแต็กของเธรดเองและไม่ถูกแชร์ดังนั้นเราต้องใช้ ThreadLocal ในกรณีนี้หรือไม่ ฉันอาจเข้าใจผิดไปโดยสิ้นเชิงโปรดให้คำแนะนำ
Jayesh

4
หากแต่ละเธรดต้องการมีสำเนาของตนเองทำไมไม่สามารถประกาศเฉพาะที่ในตัวเครื่อง (ซึ่งเป็นเธรดที่ปลอดภัยเสมอ)
sudeepdino008

@ sudeepdino008 คุณไม่สามารถควบคุมการสร้างเธรดเหล่านั้นได้เสมอ (กรอบแอปพลิเคชันเว็บ, ไลบรารี
เธรดพูล

10

เซิร์ฟเวอร์ Webapp อาจเก็บเธรดพูลไว้และThreadLocalควรลบ var ออกก่อนตอบกลับไปยังไคลเอ็นต์ดังนั้นเธรดปัจจุบันอาจถูกนำมาใช้ใหม่โดยการร้องขอครั้งต่อไป


8

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

// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
        ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});

    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
}

โปรดทราบว่าที่นี่มีการใช้วิธีการเริ่มต้นโดยใช้การแสดงออกแลมบ์ดา
2- กรณีการใช้งานอื่นคือเมื่อเราต้องการให้มีอินสแตนซ์ของเธรดที่ปลอดภัยและเราไม่ต้องการใช้การซิงโครไนซ์เนื่องจากต้นทุนการทำงานที่มีการซิงโครไนซ์เพิ่มขึ้น กรณีหนึ่งคือเมื่อใช้ SimpleDateFormat เนื่องจาก SimpleDateFormat ไม่ปลอดภัยสำหรับเธรดดังนั้นเราจึงต้องจัดเตรียมกลไกเพื่อทำให้เธรดปลอดภัย

public class ThreadLocalDemo1 implements Runnable {
    // threadlocal variable is created
    private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue(){
            System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
            return new SimpleDateFormat("dd/MM/yyyy");
        }
    };

    public static void main(String[] args) {
        ThreadLocalDemo1 td = new ThreadLocalDemo1();
        // Two threads are created
        Thread t1 = new Thread(td, "Thread-1");
        Thread t2 = new Thread(td, "Thread-2");
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
        System.out.println("Thread run execution started for " + Thread.currentThread().getName());
        System.out.println("Date formatter pattern is  " + dateFormat.get().toPattern());
        System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
    } 

}

ฉันยังไม่เห็นประโยชน์ของการใช้ ThreadLocal เพื่อสร้าง id ที่ไม่ซ้ำกันในตัวอย่างเดียวถ้าวัตถุประสงค์ของคุณคือคืนค่าจำนวนเต็มเฉพาะที่เพิ่มขึ้น `` `คลาสสาธารณะ ThreadId {// จำนวนเต็มอะตอมมิกที่มี ID เธรดถัดไปที่จะกำหนดเป็นส่วนตัวสุดท้ายคงที่ AtomicInteger nextId = ใหม่ AtomicInteger (0); // ส่งคืน ID เฉพาะของเธรดปัจจุบันกำหนดถ้าจำเป็นคงที่สาธารณะ int รับ () {return nextId..getAndIncrement (); }} `` `
dashenswen

7

ตั้งแต่ Java 8 รีลีสมีวิธีการประกาศเพิ่มเติมเพื่อเริ่มต้นThreadLocal:

ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");

ก่อนที่จะปล่อย Java 8 คุณต้องทำสิ่งต่อไปนี้:

ThreadLocal<String> local = new ThreadLocal<String>(){
    @Override
    protected String initialValue() {
        return "init value";
    }
};

นอกจากนี้หากวิธีการสร้างอินสแตนซ์ (ตัวสร้างวิธีการจากโรงงาน) ของคลาสที่ใช้ThreadLocalไม่ได้ใช้พารามิเตอร์ใด ๆ คุณสามารถใช้การอ้างอิงวิธีการ (แนะนำใน Java 8):

class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}

ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);

หมายเหตุ: การ ประเมินผลเป็นสันหลังยาวเนื่องจากคุณผ่านjava.util.function.Supplierแลมบ์ดาที่ได้รับการประเมินก็ต่อเมื่อThreadLocal#getถูกเรียกเท่านั้น แต่ยังไม่ได้ประเมินค่าก่อนหน้านี้


5

คุณต้องระวังอย่างมากกับรูปแบบ ThreadLocal มีบางด้านที่สำคัญเช่นฟิลที่กล่าวถึง แต่สิ่งที่ไม่ได้กล่าวถึงคือการทำให้แน่ใจว่ารหัสที่ตั้งค่าบริบทของ ThreadLocal ไม่ใช่ "ผู้เข้าร่วมใหม่"

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


2
การเข้าร่วมอีกครั้งจะไม่เกิดปัญหาหากมีการเตรียมรหัสเพื่อจัดการกับมัน ในรายการให้สังเกตว่าตัวแปรได้ถูกตั้งค่าไว้แล้วและเมื่อออกให้กู้คืนค่าก่อนหน้า (ถ้ามี) หรือลบออก (ถ้าไม่ใช่)
supercat

1
@ เจฟฟ์เพื่อนนั่นเป็นความจริงทุกรหัสที่คุณเขียนไม่ใช่แค่ThreadLocalรูปแบบ หากคุณทำF(){ member=random(); F2(); write(member); }และ F2 แทนที่สมาชิกด้วยค่าใหม่เห็นได้ชัดว่าwrite(member)จะไม่เขียนหมายเลขที่คุณได้random()เอ็ด นี่เป็นเพียงสามัญสำนึก ในทำนองเดียวกันถ้าคุณทำเช่นF(){ F(); }นั้นโชค gd กับวงวนไม่สิ้นสุดของคุณ! ThreadLocalนี่คือความจริงทุกที่และไม่ได้เฉพาะการ
Pacerier

@ เจฟฟ์ตัวอย่างรหัส?
Pacerier

5

เมื่อไหร่?

เมื่อวัตถุไม่ปลอดภัยต่อเธรดแทนการซิงโครไนซ์ซึ่งขัดขวางความสามารถในการปรับขนาดได้ให้วัตถุหนึ่งเดียวกับทุกเธรดและเก็บขอบเขตของเธรดซึ่งเป็น ThreadLocal หนึ่งในวัตถุที่ใช้บ่อยที่สุด แต่ไม่ใช่วัตถุที่ปลอดภัยของเธรดคือการเชื่อมต่อฐานข้อมูลและ JMSConnection

ได้อย่างไร

ตัวอย่างหนึ่งคือ Spring Framework ใช้ ThreadLocal อย่างมากสำหรับการจัดการธุรกรรมที่อยู่เบื้องหลังโดยเก็บวัตถุการเชื่อมต่อเหล่านี้ไว้ในตัวแปร ThreadLocal ในระดับสูงเมื่อเริ่มทำธุรกรรมจะได้รับการเชื่อมต่อ (และปิดใช้งานการส่งอัตโนมัติ) และเก็บไว้ใน ThreadLocal บนการเรียก db เพิ่มเติมจะใช้การเชื่อมต่อเดียวกันเพื่อสื่อสารกับ db ในตอนท้ายจะใช้การเชื่อมต่อจาก ThreadLocal และกระทำ (หรือย้อนกลับ) การทำธุรกรรมและปล่อยการเชื่อมต่อ

ฉันคิดว่า log4j ยังใช้ ThreadLocal สำหรับการบำรุงรักษา MDC


5

ThreadLocal มีประโยชน์เมื่อคุณต้องการมีสถานะบางอย่างที่ไม่ควรใช้ร่วมกันระหว่างเธรดที่แตกต่างกัน แต่ควรสามารถเข้าถึงได้จากแต่ละเธรดตลอดอายุการใช้งาน

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

ที่กล่าวไว้ในใจว่าThreadLocalเป็นรูปแบบของรัฐทั่วโลก เป็นผลให้มันมีความหมายอื่น ๆ อีกมากมายและควรใช้เฉพาะหลังจากที่พิจารณาโซลูชั่นที่เป็นไปได้อื่น ๆ ทั้งหมด


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

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

สามารถเป็นส่วนหนึ่งของสถานะวัตถุได้โดยไม่จำเป็นต้องใช้ทั่วโลก แน่นอนว่าคุณจะได้รับค่าใช้จ่ายนิด ๆ หน่อย ๆ แต่ถ้าวัตถุหลายจะต้องมีสถานะที่แตกต่างกันต่อด้ายคุณสามารถใช้ThreadLocalเป็นข้อมูลวัตถุ ...
Enerccio

คุณถูก. ฉันอาจสะดุดกับการใช้งานที่ผิดพลาดหลายประการThreadLocalซึ่งสามารถเข้าถึงได้ทั่วโลก อย่างที่คุณพูดมันยังสามารถใช้เป็นฟิลด์คลาสที่ จำกัด การมองเห็นได้
Dimos

4

ไม่มีอะไรใหม่จริงๆที่นี่ แต่ฉันค้นพบในวันนี้ว่าThreadLocalมีประโยชน์มากเมื่อใช้การตรวจสอบความถูกต้อง Bean ในเว็บแอปพลิเคชัน ข้อความการตรวจสอบจะมีการแปล Locale.getDefault()แต่จากการใช้ค่าเริ่มต้น คุณสามารถกำหนดค่าValidatorกับการที่แตกต่างกันMessageInterpolatorแต่มีวิธีที่จะระบุไม่มีเมื่อคุณเรียกLocale validateดังนั้นคุณสามารถสร้างแบบคงที่ThreadLocal<Locale>(หรือดีกว่ายังภาชนะทั่วไปที่มีสิ่งอื่น ๆ ที่คุณอาจจะต้องมีThreadLocalแล้วMessageInterpolatorเลือกกำหนดเองของคุณLocaleจากนั้นขั้นตอนต่อไปคือการเขียนServletFilterซึ่งใช้ค่าเซสชั่นหรือrequest.getLocale()เลือกสถานที่และร้านค้า ในThreadLocalการอ้างอิงของคุณ


4

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

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

รอบการร้องขอ / ตอบสนองของการดำเนินการในการโทร Java EE ส่วนใหญ่ทำให้การใช้งานประเภทนี้ง่ายเนื่องจากให้จุดเข้าและออกที่กำหนดไว้อย่างดีเพื่อตั้งค่าและยกเลิกการตั้งค่า ThreadLocal


4

ThreadLocal จะตรวจสอบให้แน่ใจว่าการเข้าถึงออบเจ็กต์ที่ไม่แน่นอนโดยหลายเธรดในวิธีการซิงโครไนซ์ที่ไม่ซิงโครไนซ์หมายถึงการทำให้ออบเจ็กต์ที่เปลี่ยนแปลงไม่ได้

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

โดยคำจำกัดความ:

คลาส ThreadLocal ใน Java ช่วยให้คุณสร้างตัวแปรที่สามารถอ่านและเขียนโดยเธรดเดียวกันเท่านั้น ดังนั้นแม้ว่าเธรดสองตัวกำลังเรียกใช้รหัสเดียวกันและรหัสนั้นมีการอ้างอิงถึงตัวแปร ThreadLocal ดังนั้นทั้งสองเธรดจะไม่สามารถเห็นตัวแปร ThreadLocal ของกันและกันได้

แต่ละThreadใน java มีThreadLocalMapอยู่ในนั้น
ที่ไหน

Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.

บรรลุ ThreadLocal:

ตอนนี้สร้างคลาส wrapper สำหรับ ThreadLocal ซึ่งจะถือวัตถุที่ไม่แน่นอนเช่นด้านล่าง (มีหรือไม่มีinitialValue())
ตอนนี้ getter และ setter ของ wrapper นี้จะทำงานบนอินสแตนซ์ของ threadlocal แทนที่จะเป็นอ็อบเจกต์ที่ไม่แน่นอน

หาก getter () ของ threadlocal ไม่พบค่าใด ๆ ใน threadlocalmap ของThread; จากนั้นมันจะเรียกใช้ initialValue () เพื่อรับสำเนาส่วนตัวที่เกี่ยวกับเธรด

class SimpleDateFormatInstancePerThread {

    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                UUID id = UUID.randomUUID();
                @Override
                public String toString() {
                    return id.toString();
                };
            };
            System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
            return dateFormat;
        }
    };

    /*
     * Every time there is a call for DateFormat, ThreadLocal will return calling
     * Thread's copy of SimpleDateFormat
     */
    public static DateFormat getDateFormatter() {
        return dateFormatHolder.get();
    }

    public static void cleanup() {
        dateFormatHolder.remove();
    }
}

ตอนนี้wrapper.getDateFormatter()จะเรียกthreadlocal.get()และที่จะตรวจสอบการcurrentThread.threadLocalMapมีนี้ (ThreadLocal) ตัวอย่างเช่น
ถ้าใช่คืนค่า (SimpleDateFormat) สำหรับอินสแตนซ์ threadlocal ที่สอดคล้องกัน
เพิ่มแผนที่ด้วยอินสแตนซ์ของ threadlocal นี้ initialValue ()

พร้อมกับความปลอดภัยของด้ายในชั้นที่ไม่แน่นอนนี้ โดยแต่ละเธรดกำลังทำงานกับอินสแตนซ์ที่เปลี่ยนแปลงได้ของตัวเอง แต่มีอินสแตนซ์ของเธรดเธรดเดียวกัน หมายถึงเธรดทั้งหมดจะใช้อินสแตนซ์ของ ThreadLocal เดียวกันกับคีย์ แต่อินสแตนซ์ SimpleDateFormat ที่แตกต่างกันเป็นค่า

https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java


3

ตัวแปรเธรดโลคัลมักถูกใช้เพื่อป้องกันการแบ่งใช้ในการออกแบบโดยยึดตาม Singletons ที่ไม่แน่นอนหรือตัวแปรโกลบอล

มันสามารถใช้ในสถานการณ์เช่นการแยกการเชื่อมต่อ JDBC สำหรับแต่ละหัวข้อเมื่อคุณไม่ได้ใช้กลุ่มการเชื่อมต่อ

private static ThreadLocal<Connection> connectionHolder
           = new ThreadLocal<Connection>() {
      public Connection initialValue() {
           return DriverManager.getConnection(DB_URL);
          }
     };

public static Connection getConnection() {
      return connectionHolder.get();
} 

เมื่อคุณเรียกใช้ getConnection มันจะส่งคืนการเชื่อมต่อที่เกี่ยวข้องกับเธรดนั้นสามารถทำได้เช่นเดียวกันกับคุณสมบัติอื่น ๆ เช่น dateformat บริบทการทำธุรกรรมที่คุณไม่ต้องการแชร์ระหว่างเธรด

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

ลิงค์นี้อธิบายการใช้งาน ThreadLocal ได้เป็นอย่างดี


2
ในตัวอย่างนี้เป็นปัญหาสำคัญ: ใครเป็นผู้รับผิดชอบในการปิดการเชื่อมต่อ? แทนที่จะสร้างการเชื่อมต่อนี้เป็นค่าเริ่มต้นให้ผู้ใช้งานของการเชื่อมต่อสร้างการเชื่อมต่ออย่างชัดเจนและผูกเข้ากับเธรดผ่าน ThreadLocal ผู้สร้างการเชื่อมต่อนั้นยังรับผิดชอบในการปิด นี่ยังไม่ชัดเจนในตัวอย่างนี้ การสร้างและเชื่อมโยงการเชื่อมต่อสามารถถูกซ่อนอยู่ในกรอบง่าย ๆ โดยบางอย่างเช่น Transaction.begin () และ Transaction.end ()
Rick-Rainer Ludwig

2

ThreadLocalชั้นใน Java ช่วยให้คุณสร้างตัวแปรที่สามารถอ่านและเขียนโดยหัวข้อเดียวกัน ดังนั้นแม้ว่าเธรดสองตัวกำลังเรียกใช้งานรหัสเดียวกันและรหัสนั้นมีการอ้างอิงถึงตัวแปร ThreadLocal ดังนั้นทั้งสองเธรดจะไม่สามารถเห็นตัวแปร ThreadLocal ของกันและกันได้

อ่านเพิ่มเติม


2

มี 3 สถานการณ์สำหรับการใช้ตัวช่วยคลาสเช่น SimpleDateFormat ในรหัสแบบหลายเธรดซึ่งสิ่งที่ดีที่สุดคือใช้ThreadLocal

สถานการณ์

1-การใช้Like share objectโดยใช้กลไกการล็อคหรือการซิงโครไนซ์ซึ่งทำให้แอปช้าลง

2-การใช้เป็นวัตถุท้องถิ่นภายในวิธีการ

ในสถานการณ์สมมตินี้ถ้าเรามี4 เธรดแต่ละคนเรียกเมธอด 1000ครั้งเราจะสร้างวัตถุ SimpleDateFormat
4000 รายการและรอให้ GC ลบทิ้ง

3-การใช้ThreadLocal

ถ้าเรามี 4 ด้ายและเราให้กับแต่ละกรณี SimpleDateFormat ด้ายหนึ่ง
เพื่อให้เรามี4 กระทู้ , 4 วัตถุของ SimpleDateFormat

ไม่จำเป็นต้องใช้กลไกการล็อคและการสร้างและทำลายวัตถุ (ความซับซ้อนของเวลาที่ดีและความซับซ้อนของพื้นที่)


2

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


1

การแคชบางครั้งคุณต้องคำนวณค่าจำนวนมากเวลาเดียวกันโดยการเก็บชุดของอินพุตสุดท้ายไปยังวิธีการและผลลัพธ์ที่คุณสามารถเพิ่มความเร็วรหัส ด้วยการใช้ Thread Local Storage คุณไม่ต้องคิดถึงการล็อค


1

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

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;


public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);

// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());


// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
    return threadId.get();
}

public static void main(String[] args) {

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

}
}

1

Threadlocal เป็นวิธีที่ง่ายมากในการทำให้วัตถุกลับมาใช้ใหม่ได้โดยไม่มีค่าใช้จ่าย

ฉันมีสถานการณ์ที่หลายเธรดกำลังสร้างภาพแคชที่ไม่แน่นอนในการแจ้งเตือนการอัปเดตแต่ละครั้ง

ฉันใช้ Threadlocal ในแต่ละเธรดแล้วแต่ละเธรดจะต้องรีเซ็ตภาพเก่าแล้วอัปเดตอีกครั้งจากแคชในการแจ้งเตือนการอัปเดตแต่ละครั้ง

วัตถุที่นำกลับมาใช้ใหม่ได้ตามปกติจากกลุ่มวัตถุมีค่าใช้จ่ายด้านความปลอดภัยของเธรดที่เกี่ยวข้องกับวัตถุเหล่านั้นในขณะที่วิธีนี้ไม่มีเลย


0

ลองตัวอย่างเล็ก ๆ นี้เพื่อให้เข้าใจถึงตัวแปรของ ThreadLocal:

public class Book implements Runnable {
    private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);

    private final String bookName; // It is also the thread's name
    private final List<String> words;


    public Book(String bookName, List<String> words) {
        this.bookName = bookName;
        this.words = Collections.unmodifiableList(words);
    }

    public void run() {
        WORDS.get().addAll(words);
        System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
        Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
        t1.start();
        t2.start();
    }
}


เอาต์พุตคอนโซลหากเธรด BookA เสร็จสิ้นก่อน
ผลลัพธ์ผลลัพธ์ A: 'wordA1, wordA2, wordA3'
ผลการสืบค้น B: 'wordB1, wordB2'

เอาต์พุตคอนโซลหากเธรด BookB เสร็จสิ้นก่อน
ผลลัพธ์ BookB: 'wordB1, wordB2'
ผลการสืบค้น A: 'wordA1, wordA2, wordA3'

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