ยังมีความแตกต่างระหว่างตัวแปร ThreadLocal และ Thread Member คลาส ThreadLocal มีตัวแปรโลคัลเธรด ตัวแปรท้องถิ่นนี้แตกต่างจากตัวแปรสมาชิกทั่วไป เมื่อตัวแปร ThreadLocal ถูกใช้โดยหลายเธรดแต่ละเธรดแต่ละเธรดสามารถได้รับหนึ่งสำเนาของตัวแปรเท่านั้น นี่คือคำอธิบายใน Java API โดยการอ่านซอร์สโค้ด API ฉันพบว่าไม่ใช่สำเนา แนวคิดของสำเนาคืออะไร? โคลน? หรืออย่างอื่นคลุมเครือเกินไป
เพื่อความแม่นยำรีจิสทรี (แผนที่ <เธรด, t>) ภายในตัวแปรของประเภท ThreadLocal มีการเปลี่ยนแปลง แต่ตัวแปรของประเภท ThreadLocal นั้นเป็นหนึ่งเดียวและนี่คือสาระสำคัญ!
นี่คือตัวอย่าง:
1. ตัวอย่างมาตรฐาน
คลาส MythreadLocal ถูกกำหนดและสร้างวัตถุ TLT และใช้โดยสี่เธรด เป็นผลให้ตัวแปร TLT ของสี่เธรดไม่แบ่งปัน ประการที่สองคือการใช้ของตัวเองซึ่งแสดงให้เห็นว่าสี่เธรดใช้สำเนาของ TLT (โคลน)
/*** ใช้คลาส ThreadLocal*/คลาสสาธารณะ MyThreadLocal {// กำหนดตัวแปร ThreadLocal เพื่อบันทึก int หรือ integer data private threadLocal <integer> tl = new ThreadLocal <จำนวนเต็ม> () {@Override - จำนวนเต็มสาธารณะ getNextNum () {// รับค่าของ TL และเพิ่ม 1 และอัปเดตค่าของ t1 tl.Set (tl.get () + 1); กลับ tl.get (); - / *** ทดสอบเธรด*/ Public Class TestThread ขยายเธรด {Private MyThreadLocal TLT = ใหม่ MyThreadLocal (); Public TestThread (MyThreadLocal tlt) {this.tlt = tlt; } @Override โมฆะสาธารณะ Run () {สำหรับ (int i = 0; i <3; i ++) {system.out.println (thread.currentthread (). getName () + "/t" + tlt.getNextNum ()); - / *** การทดสอบ threadlocal*/ การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {mythreadlocal tlt = ใหม่ mythreadlocal (); เธรด t1 = testThread ใหม่ (TLT); เธรด t2 = testThread ใหม่ (TLT); เธรด t3 = testThread ใหม่ (TLT); เธรด t4 = testThread ใหม่ (TLT); t1.start (); t2.start (); t3.start (); t4.start (); -
จะเห็นได้ว่าทั้งสามเธรดนั้นมีหมายเลขอย่างอิสระและไม่ส่งผลต่อกันและกัน:
เธรด -0 1 เธรด -1 1 เธรด -0 2 เธรด -1 2 เธรด -0 3 เธรด -1 3 เธรด -2 1 เธรด -3 1 เธรด 2 2 เธรด -3 2 เธรด -2 3 เธรด -3 3 กระบวนการเสร็จสิ้นด้วยรหัสออก 0
วัตถุ TLT เป็นหนึ่งเดียวและวัตถุ TL ไร้สาระก็เป็นหนึ่งเดียวเพราะความสัมพันธ์แบบผสมผสานเป็นแบบหนึ่งต่อหนึ่ง อย่างไรก็ตามเมื่อจำนวนเธรดเพิ่มขึ้นวัตถุจำนวนเต็มจำนวนมากจะถูกสร้างขึ้น มันเป็นเพียงจำนวนเต็มและ int นั้นเป็นเรื่องธรรมดาอยู่แล้ว ดังนั้นฉันไม่สามารถรู้สึกถึงคุณสมบัติของวัตถุของจำนวนเต็ม
2. อย่าใช้ Threadlocal
หากคุณไม่ได้ใช้ ThreadLocal คุณเพียงแค่ต้องกำหนดคลาส MythreadLocal ใหม่เป็น:
/ *** ใช้คลาส ThreadLocal*/ คลาสสาธารณะ MyThreadLocal {Integer ส่วนตัว T1 = 0; จำนวนเต็มสาธารณะ getNextNum () {return t1 = t1+1; } // กำหนดตัวแปร ThreadLocal เพื่อบันทึกข้อมูล int หรือจำนวนเต็ม // threadlocal ส่วนตัว <teger> tl = threadLocal ใหม่ <จำนวนเต็ม> () {// @Override // การป้องกันจำนวนเต็มเริ่มต้น () {// return 0; - // // Public Integer getNextNum () {// // รับค่าของ TL และเพิ่ม 1 และอัปเดตค่าของ T1 // TL.Set (tl.get () + 1); // return tl.get (); -
จากนั้นเรียกใช้การทดสอบ:
เธรด -2 1 เธรด -2 2 เธรด -1 4 เธรด -1 6 เธรด -3 3 เธรด -3 9 เธรด -3 10 เธรด -1 8 เธรด -0 7 เธรด -0 11 เธรด -0 12 เธรด 2 5 กระบวนการเสร็จสิ้นด้วยรหัสออก 0
จากที่นี่เราจะเห็นได้ว่าเธรดทั้งสี่แบ่งปันตัวแปร TLT และแต่ละเธรดจะปรับเปลี่ยนคุณสมบัติของ TLT โดยตรง
3. ตระหนักถึง Threadlocal ด้วยตัวเอง
แพ็คเกจ com.lavasoft.test2; นำเข้า Java.util.Collections; นำเข้า java.util.hashmap; นำเข้า java.util.map; /*** ใช้คลาส ThreadLocal*/คลาสสาธารณะ MyThreadLocal {// กำหนดตัวแปร ThreadLocal เพื่อบันทึกข้อมูล int หรือจำนวนเต็มส่วนตัว com.lavasoft.test2.threadlocal <teeger> tl = new com.lavasoft.test2.threadlocal }}; จำนวนเต็มสาธารณะ getNextNum () {// รับค่าของ TL และเพิ่ม 1 และอัปเดตค่าของ t1 tl.Set (tl.get () + 1); กลับ tl.get (); }} คลาส ThreadLocal <t> {แผนที่ส่วนตัว <เธรด, t> map = collections.synchronizedmap (ใหม่ hashmap <เธรด, t> ()); Public ThreadLocal () {} ได้รับการป้องกัน t initialValue () {return null; } สาธารณะ t get () {เธรด t = thread.currentthread (); t obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } ชุดโมฆะสาธารณะ (ค่า t) {map.put (thread.currentthread (), ค่า); } โมฆะสาธารณะลบ () {map.remove (thread.currentthread ()); -
เรียกใช้การทดสอบ:
เธรด -0 1 เธรด -0 2 เธรด -0 3 เธรด 2 1 เธรด 2 2 เธรด -3 1 เธรด -2 3 เธรด -3 2 เธรด -1 1 เธรด -3 3 เธรด -1 2 เธรด -1 3 กระบวนการเสร็จสิ้นด้วยรหัสออก 0
น่าแปลกที่ ThreadLocal เวอร์ชันเลียนแบบนี้ใช้งานได้ดีโดยใช้ฟังก์ชั่นของ ThreadLocal ใน Java API
4. ดูสาระสำคัญผ่านปรากฏการณ์
ในความเป็นจริงจากมุมมองของโปรแกรมตัวแปร TLT นั้นเป็นหนึ่งเดียวโดยไม่ต้องสงสัยเลย แต่ทำไมตัวเลขที่พิมพ์ไม่ส่งผลต่อกันและกัน
เป็นเพราะการใช้จำนวนเต็มหรือไม่? -----เลขที่.
เหตุผลคือ: ได้รับการป้องกัน t itiedValue () และ get () เพราะเมื่อการโทรแต่ละเธรดได้รับ () มันจะสร้างมันถ้าไม่มีอยู่ในแผนที่ เมื่อมีการเรียกว่าตัวแปรใหม่จะถูกสร้างขึ้นด้วย Type T ทุกครั้งที่พวกเขาถูกสร้างขึ้นใหม่แน่นอนแต่ละคนใช้มันโดยไม่มีผลใด ๆ ซึ่งกันและกัน
เพื่อที่จะเห็นสาระสำคัญอย่างชัดเจนให้แทนที่จำนวนเต็มและเขียนคลาสบางส่วน:
แพ็คเกจ com.lavasoft.test2; นำเข้า Java.util.Collections; นำเข้า java.util.hashmap; นำเข้า java.util.map; /*** ใช้คลาส ThreadLocal*/คลาสสาธารณะ MyThreadLocal {// กำหนดตัวแปร ThreadLocal เพื่อบันทึก int หรือ integer data // private ThreadLocal <bean> tl = threadlocal ใหม่ <bean> () {ส่วนตัว com.lavasoft.test2.threadlocal @Override ป้องกันถั่วเริ่มต้น () {ส่งคืนถั่วใหม่ (); - @Override สตริงสาธารณะ toString () {return "MyThreadLocal {" + "tl =" + tl + '}'; } ถั่วสาธารณะ getBean () {return tl.get (); }} คลาส ThreadLocal <t> {แผนที่ส่วนตัว <เธรด, t> map = collections.synchronizedmap (ใหม่ hashmap <เธรด, t> ()); Public ThreadLocal () {} ได้รับการป้องกัน t initialValue () {return null; } สาธารณะ t get () {เธรด t = thread.currentthread (); t obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } ชุดโมฆะสาธารณะ (ค่า t) {map.put (thread.currentthread (), ค่า); } โมฆะสาธารณะลบ () {map.remove (thread.currentthread ()); - แพ็คเกจ com.lavasoft.test2; / ** * ทดสอบถั่ว */ ถั่วคลาสสาธารณะ {string private id = "0"; ชื่อสตริงส่วนตัว = "ไม่มี"; ถั่วสาธารณะ () {} ถั่วสาธารณะ (รหัสสตริง, ชื่อสตริง) {this.id = id; this.name = ชื่อ; } สตริงสาธารณะ getId () {return id; } โมฆะสาธารณะ setId (รหัสสตริง) {this.id = id; } สตริงสาธารณะ getName () {ชื่อคืน; } โมฆะสาธารณะ setName (ชื่อสตริง) {this.name = name; } สตริงสาธารณะ showinfo () {return "Bean {" + "id = '" + id +'/'' + ", name = '" + name +'/'' + '}'; - แพ็คเกจ com.lavasoft.test2; / *** ทดสอบเธรด*/ Public Class TestThread ขยายเธรด {Private MyThreadLocal TLT = ใหม่ MyThreadLocal (); Public TestThread (MyThreadLocal tlt) {this.tlt = tlt; } @Override โมฆะสาธารณะเรียกใช้ () {system.out.println (">>>>:" + tlt); สำหรับ (int i = 0; i <3; i ++) {system.out.println (thread.currentthread (). getName ()+"/t"+tlt.getBean ()+"/t"+tlt.getBean (). showinfo (); -
จากนั้นเรียกใช้การทดสอบ:
>>>>>>: mythreadlocal {tl=com.lavasoft.test2.mythreadlocal$1@1de3f2d} >>>>>>: mythreadlocal {tl=com.lavasoft.test2.mythrecal$1@1de3f2d >>>>>: MyThreadLocal {tl=com.lavasoft.test2.bean@291aff Bean {id = '0', name = 'none'} thread-2 com.lavasoft.test2.bean@fe64b9 Bean {id = '0' Bean {id = '0', name = 'none'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'none'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0' Bean {id = '0', name = 'none'} Thread-0 com.lavasoft.test2.bean@291aff Bean {id = '0', name = 'none'} thread-3 com.lavasoft.test2.bean@186db54 bean {id = '0' com.lavasoft.test2.bean@186db54 Bean {id = '0', name = 'none'} thread-1 com.lavasoft.test2.bean@291aff Bean {id = '0', name = 'none'} thread-0 com.lavasoft.test2.bean@291aff com.lavasoft.test2.bean@291aff Bean {id = '0', name = 'none'} thread-1 com.lavasoft.test2.bean@291aff Bean {id = '0', name = 'none'
เป็นที่ชัดเจนจากผลการพิมพ์ว่าวัตถุ TLT ของ MythreadLocal เป็นหนึ่งเดียวและวัตถุ TL ของ ThreadLocal ในวัตถุ TLT ก็เป็นหนึ่ง อย่างไรก็ตามเมื่อใช้ T1T สำหรับแต่ละเธรดเธรดจะสร้างวัตถุถั่วขึ้นใหม่และเพิ่มลงในแผนที่ ThreadLocal เพื่อใช้งาน
ความเข้าใจผิดหลายประการเกี่ยวกับ Threadlocal:
1. Threadlocal เป็นการใช้งานของกระทู้ Java
Threadlocal นั้นเกี่ยวข้องกับเธรด Java แต่ไม่ใช่การใช้งานของกระทู้ Java มันถูกใช้เพื่อรักษาตัวแปรท้องถิ่น สำหรับแต่ละเธรดจะมีเวอร์ชันตัวแปรของตัวเองเป็นหลักเพื่อหลีกเลี่ยงความขัดแย้งของเธรดและแต่ละเธรดจะรักษาเวอร์ชันของตัวเอง เป็นอิสระจากกันและกันและการดัดแปลงจะไม่ส่งผลต่อกันและกัน
2. Threadlocal สัมพันธ์กับแต่ละเซสชัน
Threadlocal ตามชื่อหมายถึงมีจุดมุ่งหมายที่เธรด ในการเขียนโปรแกรมเว็บ Java ผู้ใช้แต่ละคนมีตัวระบุเซสชันของตัวเองตั้งแต่ต้นจนถึงสิ้นสุดเซสชัน แต่ Threadlocal ไม่ได้อยู่ในเลเยอร์เซสชัน ในความเป็นจริง ThreadLocal เป็นอิสระจากเซสชันผู้ใช้ มันเป็นพฤติกรรมฝั่งเซิร์ฟเวอร์ เมื่อใดก็ตามที่เซิร์ฟเวอร์สร้างเธรดใหม่จะรักษา ThreadLocal ของตัวเองไว้
เกี่ยวกับความเข้าใจผิดนี้ฉันเชื่อว่าควรเป็นผลมาจากการทดสอบในท้องถิ่นของนักพัฒนาโดยใช้เซิร์ฟเวอร์แอปพลิเคชันบางตัว อย่างที่เราทราบกันดีว่าแอปพลิเคชันเซิร์ฟเวอร์ทั่วไปจะรักษาชุดพูลเธรดนั่นคือสำหรับการเข้าถึงแต่ละเธรดใหม่ไม่จำเป็นต้องสร้าง ฉันมีพูลแคชเธรดแทน สำหรับการเข้าถึงก่อนอื่นให้ค้นหาเธรดที่มีอยู่จากพูลแคช หากพวกเขาใช้งานได้ทั้งหมดแล้วเธรดใหม่จะถูกสร้างขึ้น
ดังนั้นเนื่องจากนักพัฒนามักจะเป็นคนเดียวที่ทดสอบตัวเองภาระของเซิร์ฟเวอร์จึงมีขนาดเล็กมากซึ่งนำไปสู่การแบ่งปันเธรดเดียวกันทุกครั้งที่การเข้าถึงเป็นผลให้เกิดความเข้าใจผิด: แต่ละเซสชัน
3. Threadlocal สัมพันธ์กับแต่ละเธรด ทุกครั้งที่ผู้ใช้เข้าถึงจะมี ThreadLocal ใหม่
ในทางทฤษฎี Threadlocal นั้นสัมพันธ์กับแต่ละเธรดแต่ละเธรดจะมี Threadlocal ของตัวเอง แต่ดังที่ได้กล่าวไว้ข้างต้นแอปพลิเคชันเซิร์ฟเวอร์ทั่วไปจะรักษาชุดของพูลเธรด ดังนั้นผู้ใช้ที่แตกต่างกันอาจได้รับเธรดเดียวกัน ดังนั้นเมื่อทำตามส่วนหัวคุณจะต้องระมัดระวังในการหลีกเลี่ยงแคชของตัวแปร ThreadLocal ทำให้เธรดอื่น ๆ เข้าถึงตัวแปรเธรด
4. สำหรับการเข้าถึงผู้ใช้แต่ละครั้งสามารถใช้ ThreadLocal ได้หลายครั้ง
อาจกล่าวได้ว่า Threadlocal เป็นดาบสองคมและสามารถมีผลลัพธ์ที่ดีมากหากใช้ อย่างไรก็ตามหาก Threadlocal ใช้งานไม่ได้ดีมันจะเหมือนกับตัวแปรทั่วโลก รหัสไม่สามารถนำกลับมาใช้ใหม่และไม่สามารถทดสอบได้อย่างอิสระ เพราะบางคลาสที่สามารถนำกลับมาใช้ใหม่ได้ในขณะนี้ขึ้นอยู่กับตัวแปร ThreadLocal หากไม่มี Threadlocal คลาสเหล่านี้จะไม่สามารถใช้งานได้ โดยส่วนตัวฉันคิดว่า ThreadLocal ใช้งานได้ดีและคุ้มค่าที่จะอ้างถึง
1. จัดเก็บผู้ใช้เซสชันปัจจุบัน: Quake Want Jert
2. เก็บตัวแปรบริบทบางอย่างเช่น ActionContext
3. เซสชันร้านค้าเช่นฤดูใบไม้ผลิ Hibernate ORM