JavaRush /Java блогу /Random-KY /Java тилиндеги PhantomReference

Java тилиндеги PhantomReference

Группада жарыяланган
Салам! Бүгүнкү сабакта биз Java тorндеги Phantom References жөнүндө кеңири сүйлөшөбүз. Бул кандай шилтемелер, эмне үчүн алар "фантомдук" деп аталат жана аларды кантип колдонуу керек? Эсиңизде болсун, Javaда шилтемелердин 4 түрү бар:
  1. StrongReference (an objectти түзүүдө биз түзгөн кадимки шилтемелер):

    Cat cat = new Cat()

    бул мисалда мышык күчтүү шилтеме болуп саналат.

  2. SoftReference (жумшак шилтеме). Биз бул шилтемелер тууралуу лекция өткөрдүк.

  3. WeakReference (алсыз шилтеме). Бул жерде алар жөнүндө да лекция болду .

  4. PhantomReference (фантом шилтеме).

Акыркы үч түрдөгү an objectтер терилген (мисалы, SoftReference<Integer> , WeakReference<MyClass> ). Класстар SoftReferenceжана класстан мурас WeakReference. Бул класстар менен иштөөдө эң маанилүү ыкмалар: PhantomReferenceReference
  • get()— бул шилтеме тиешелүү an objectти кайтарат;

  • clear()— an objectке шилтемени жок кылат.

SoftReferenceСиз жана жөнүндө лекциялардан бул ыкмаларды эстейсиз WeakReference. Алар шилтемелердин ар кандай түрлөрү менен ар кандай иштешерин эстен чыгарбоо маанилүү. Бүгүн биз майда-чүйдөсүнө чейин алгачкы үч түрүн карап эмес, бирок фантом шилтемелер жөнүндө сөз болот. Биз шилтемелердин башка түрлөрүнө да токтолобуз, бирок фантомдук шилтемелер алардан кандайча айырмаланары жөнүндө сөз кыла турган бөлүгүндө гана. Go! :) Эмне үчүн бизге фантомдук шилтемелер керек экенинен баштайлы. Белгилүү болгондой, таштанды жыйноочу (Garbage Collector же gc) эстутумду керексиз Java an objectилеринен бошотуу үчүн жооптуу . Коллектор an objectини эки «өтүү» менен алып салат. Биринчи өтүүдө ал an objectтерди гана карап, керек болсо, аларды "керексиз жана жок кылынат" деп белгилейт. Эгерде бул an objectте жокко чыгарылган ыкма болсо finalize(), ал деп аталат. Же аталbyte - сиздин бактыңызга жараша. Бул туруксуз нерсе экени эсиңизде болсо керек finalize():) Коллектордун экинчи өтүүсүндө an object өчүрүлүп, эс тутум бошотулат. Таштанды жыйноочунун мындай күтүүсүз жүрүм-туруму биз үчүн бир катар көйгөйлөрдү жаратууда. Таштанды чыгаруучу качан иштей баштаарын так билбейбиз. Методдун чакырылышын билбейбиз finalize(). Мындан тышкары, иш учурунда finalize()an objectке күчтүү шилтеме түзүлүшү мүмкүн, андан кийин ал таптакыр жок кылынbyte. Эс тутумга муктаж системаларда бул оңой эле алып келиши мүмкүн OutOfMemoryError. Мунун баары бизди фантомдук шилтемелерди колдонууга түртөт . Кеп бул таштанды жыйноочунун жүрүм-турумун өзгөртөт. Эгерде an objectке фантомдук шилтемелер гана калса, анда ал:
  • ыкма деп аталат finalize()(эгерде ал жокко чыгарылса);

  • эгерде жумуштан кийин finalize()эч нерсе өзгөрбөсө жана an object дагы эле жок кылынса, an objectке фантомдук шилтеме атайын кезекке коюлат - ReferenceQueue.

Фантомдук шилтемелер менен иштөөдө түшүнүү керек болгон эң маанилүү нерсе, an object эстутумдан анын фантомдук шилтемеси ушул кезекте турганда өчүрүлбөйт . Ал фантомдук шилтемеден кийин гана жок кылынат clear(). Келгиле, бир мисал карап көрөлү. Биринчиден, кээ бир маалыматтарды сактай турган тест классын түзөлү.
public class TestClass {
   private StringBuffer data;
   public TestClass() {
       this.data = new StringBuffer();
       for (long i = 0; i < 50000000; i++) {
           this.data.append('x');
       }
   }
   @Override
   protected void finalize() {
       System.out.println("У an object TestClass вызван метод finalize!!!");
   }
}
Биз an objectтерди көбүрөөк эс тутум алуу үчүн аларды түзүүдө (ар бир an objectке 50 миллион "x" белгисин кошуу) маалымат менен атайылап "жүктөйбүз". finalize()Мындан тышкары, биз анын иштегенин көрүү үчүн аны атайын жокко чыгарабыз . Андан кийин бизге мураска ала турган класс керек PhantomReference. Эмне үчүн бизге мындай класс керек? Баары оңой. clear()Ушундай жол менен биз фантомдук шилтеме чындыгында тазаланганын (ошондуктан an object жок кылынганын) көрүү үчүн ыкмага кошумча логиканы кошо алабыз .
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class MyPhantomReference<TestClass> extends PhantomReference<TestClass> {

   public MyPhantomReference(TestClass obj, ReferenceQueue<TestClass> queue) {

       super(obj, queue);

       Thread thread = new QueueReadingThread<TestClass>(queue);

       thread.start();
   }

   public void cleanup() {
       System.out.println("Очистка фантомной ссылки! Удаление an object из памяти!");
       clear();
   }
}
Андан кийин бизге өзүнчө жип керек болот, ал таштанды жыйноочу өз ишин кылганга чейин күтөт жана ReferenceQueueбиздин кезекте фантомдук шилтемелер пайда болот. Мындай шилтеме кезекке киргенде, анын ыкмасы деп аталат cleanup():
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;

public class QueueReadingThread<TestClass> extends Thread {

   private ReferenceQueue<TestClass> referenceQueue;

   public QueueReadingThread(ReferenceQueue<TestClass> referenceQueue) {
       this.referenceQueue = referenceQueue;
   }

   @Override
   public void run() {

       System.out.println("Поток, отслеживающий очередь, стартовал!");
       Reference ref = null;

       //ждем, пока в очереди появятся ссылки
       while ((ref = referenceQueue.poll()) == null) {

           try {
               Thread.sleep(50);
           }

           catch (InterruptedException e) {
               throw new RuntimeException("Поток " + getName() + " был прерван!");
           }
       }

       //How только в очереди появилась фантомная link - очистить ее
       ((MyPhantomReference) ref).cleanup();
   }
}
Акыр-аягы, бизге бир ыкма керек main(): аны өзүнчө класска коелу Main. TestClassАнда биз an objectти , ага фантомдук шилтемени жана фантомдук шилтемелер үчүн кезекти түзөбүз . Андан кийин биз таштанды жыйноочуну чакырып, эмне болорун көрөбүз :)
import java.lang.ref.*;

public class Main {

   public static void main(String[] args) throws InterruptedException {
       Thread.sleep(10000);

       ReferenceQueue<TestClass> queue = new ReferenceQueue<>();
       Reference ref = new MyPhantomReference<>(new TestClass(), queue);

       System.out.println("ref = " + ref);

       Thread.sleep(5000);

       System.out.println("Вызывается сборка мусора!");

       System.gc();
       Thread.sleep(300);

       System.out.println("ref = " + ref);

       Thread.sleep(5000);

       System.out.println("Вызывается сборка мусора!");

       System.gc();
   }
}
Консолдун чыгышы: ref = MyPhantomReference@4554617c Кезекти көзөмөлдөөчү жип башталды! Таштанды чогултуу деп аталат! Finalize ыкмасы TestClass an objectисинде чакырылган!!! ref = MyPhantomReference@4554617c Таштанды чогултуу чакырылууда! Фантомдук шилтемени тазалоо! Объектти эс тутумдан алып салуу! Бул жерде биз эмнени көрүп жатабыз? Баары биз пландагандай болду! Биздин an object классыбыз жокко чыгарылган методго ээ finalize()жана ал коллектор иштеп жатканда чакырылган. Андан кийин, фантомдук шилтеме кезекке турду ReferenceQueue. Ал жерде анын методу бар болчу clear()(андан биз cleanup()консолго чыгарууну коштук). Натыйжада, an object эс тутумдан алынып салынган. Эми анын кантип иштээрин көрүп турасыз :) Албетте, фантомдук шилтемелер менен байланышкан бардык теорияны жаттап алуунун кереги жок. Бирок, жок эле дегенде, негизги ойлорду эстеп койсоңуз жакшы болот. Биринчиден , булар эң алсыз звенолор. Алар an objectке башка шилтемелер жок болгондо гана ишке кирет. Биз жогоруда келтирген шилтемелердин тизмеси күчүнүн азаюу тартибинде: StrongReference-> SoftReference-> WeakReference-> PhantomReference Фантомдук шилтеме биздин an objectибизде Күчтүү, Жумшак же Алсыз шилтемелер жок болгондо гана согушка кирет :) Экинчиден , ыкма get()фантомдук шилтеме үчүн ар дайым кайтып келет null. Бул жерде биз үч түрдүү типтеги унаалар үчүн үч түрдүү шилтемелерди түзгөн жөнөкөй мисал:
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class Main {

   public static void main(String[] args) {

       Sedan sedan = new Sedan();
       HybridAuto hybrid = new HybridAuto();
       F1Car f1car = new F1Car();

       SoftReference<Sedan> softReference = new SoftReference<>(sedan);
       System.out.println(softReference.get());

       WeakReference<HybridAuto> weakReference = new WeakReference<>(hybrid);
       System.out.println(weakReference.get());

       ReferenceQueue<F1Car> referenceQueue = new ReferenceQueue<>();

       PhantomReference<F1Car> phantomReference = new PhantomReference<>(f1car, referenceQueue);
       System.out.println(phantomReference.get());

   }
}
Консолдун чыгышы: Sedan@4554617c HybridAuto@74a14482 null Бул ыкма get()жумшак шилтеме жана алсыз шилтеме үчүн нормалдуу an objectтерди кайтарды, бирок nullфантом үчүн кайтарылды. Үчүнчүдөн , фантомдук шилтемелерди колдонуунун негизги багыты an objectтерди эс тутумдан алып салуу үчүн татаал proceduresалар болуп саналат. Баары болду! :) Ушуну менен бүгүнкү сабагыбыз жыйынтыкталды. Бирок бир гана теория сизди алыска алып барbyte, андыктан көйгөйлөрдү чечүүгө кайтып келүүгө убакыт келди! :)
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION