JavaRush /وبلاگ جاوا /Random-FA /شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت اول - ...
Viacheslav
مرحله

شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت اول - موضوعات

در گروه منتشر شد

معرفی

Multithreading از همان روزهای اولیه خود در جاوا ساخته شده است. بنابراین بیایید نگاهی گذرا به موضوع چند رشته ای بیندازیم. شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت اول - موضوعات - 1بیایید درس رسمی از Oracle را به عنوان نقطه شروع در نظر بگیریم: " درس: برنامه "Hello World! ". بیایید کد برنامه Hello World خود را کمی به زیر تغییر دهیم:
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsآرایه ای از پارامترهای ورودی است که هنگام شروع برنامه ارسال می شود. بیایید این کد را در فایلی با نامی که با نام کلاس و پسوند مطابقت دارد ذخیره کنیم .java. بیایید با استفاده از ابزار جاوا کامپایل کنیم : javac HelloWorldApp.java پس از آن، کد ما را با پارامتری فراخوانی کنید، به عنوان مثال، راجر: java HelloWorldApp Roger شما نمی توانید جاوا را با یک Thread خراب کنید: Part I - Threads - 2کد ما اکنون یک نقص جدی دارد. اگر هیچ آرگومانی را پاس نکنیم (یعنی فقط java HelloWorldApp را اجرا کنیم)، یک خطا دریافت خواهیم کرد:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
یک استثنا (به عنوان مثال یک خطا) در رشته ای به نام رخ داده است main. معلوم می شود که نوعی رشته در جاوا وجود دارد؟ این جایی است که سفر ما آغاز می شود.

جاوا و موضوعات

برای اینکه بفهمید thread چیست، باید بدانید که چگونه یک برنامه جاوا راه اندازی می شود. بیایید کد خود را به صورت زیر تغییر دهیم:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
حالا بیایید دوباره آن را با استفاده از javac کامپایل کنیم. در مرحله بعد، برای راحتی کار، کد جاوا خود را در یک پنجره جداگانه اجرا می کنیم. در ویندوز می توانید این کار را به صورت زیر انجام دهید: start java HelloWorldApp. اکنون، با استفاده از ابزار jps ، بیایید ببینیم که جاوا چه اطلاعاتی را به ما می‌گوید: شما نمی توانید جاوا را با یک Thread خراب کنید: Part I - Threads - 3اولین شماره PID یا Process ID، شناسه فرآیند است. فرآیند چیست؟
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
با کمک فرآیندها، اجرای برنامه های مختلف از یکدیگر جدا می شود: هر برنامه از منطقه حافظه خود بدون تداخل با برنامه های دیگر استفاده می کند. به شما توصیه می کنم مقاله را با جزئیات بیشتر بخوانید: " https://habr.com/post/164487/ ". یک فرآیند نمی تواند بدون رشته ها وجود داشته باشد، بنابراین اگر یک فرآیند وجود داشته باشد، حداقل یک رشته در آن وجود دارد. چگونه این اتفاق در جاوا می افتد؟ وقتی یک برنامه جاوا را اجرا می کنیم، اجرای آن با دستور شروع می شود main. به نوعی وارد برنامه می شویم، بنابراین به این روش خاص main، نقطه ورود یا «نقطه ورود» می گویند. روش mainباید همیشه public static voidبه گونه ای باشد که ماشین مجازی جاوا (JVM) بتواند برنامه ما را اجرا کند. برای جزئیات بیشتر به " چرا روش اصلی جاوا ثابت است؟ " مراجعه کنید. به نظر می رسد که لانچر جاوا (java.exe یا javaw.exe) یک برنامه کاربردی ساده است (برنامه C ساده): DLL های مختلفی را بارگذاری می کند که در واقع JVM هستند. راه‌انداز جاوا مجموعه خاصی از تماس‌های رابط بومی جاوا (JNI) را ایجاد می‌کند. JNI مکانیزمی است که دنیای ماشین مجازی جاوا و دنیای C++ را پل می کند. معلوم شد که لانچر JVM نیست، بلکه لودر آن است. دستورات صحیح را برای راه اندازی JVM می داند. می داند چگونه با استفاده از تماس های JNI، تمام محیط های لازم را سازماندهی کند. این سازماندهی محیط همچنین شامل ایجاد یک نخ اصلی است که معمولاً به آن می گویند main. برای اینکه واضح تر ببینیم چه رشته هایی در یک فرآیند جاوا زندگی می کنند، از برنامه jvisualvm استفاده می کنیم که در JDK موجود است. با دانستن pid یک فرآیند، می‌توانیم داده‌ها را فوراً روی آن باز کنیم: jvisualvm --openpid айдипроцесса شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت اول - موضوعات - 4جالب اینجاست که هر رشته دارای ناحیه جداگانه‌ای در حافظه است که برای پردازش اختصاص داده شده است. این ساختار حافظه پشته نامیده می شود. یک پشته از قاب ها تشکیل شده است. فریم نقطه فراخوانی یک متد، نقطه اجرا است. یک فریم همچنین می تواند به عنوان StackTraceElement نمایش داده شود (به Java API برای StackTraceElement مراجعه کنید ). در اینجا می توانید اطلاعات بیشتری در مورد حافظه اختصاص داده شده به هر رشته بخوانید . اگر به Java API نگاه کنیم و کلمه Thread را جستجو کنیم، می بینیم که یک کلاس java.lang.Thread وجود دارد . این کلاس است که یک جریان را در جاوا نشان می دهد و با این است که ما باید کار کنیم. Thread'ом Java не испортишь: Часть I — потоки - 5

java.lang.thread

یک رشته در جاوا به عنوان نمونه ای از کلاس نمایش داده می شود java.lang.Thread. ارزش فوراً درک این نکته را دارد که نمونه های کلاس Thread در جاوا خود نخ نیستند. این فقط نوعی API برای رشته های سطح پایین است که توسط JVM و سیستم عامل مدیریت می شوند. وقتی JVM را با استفاده از لانچر جاوا راه اندازی می کنیم، یک رشته اصلی با یک نام mainو چندین رشته سرویس دیگر ایجاد می کند. همانطور که در JavaDoc کلاس Thread گفته شد: When a Java Virtual Machine starts up, there is usually a single non-daemon thread 2 نوع thread وجود دارد: daemon و non-daemon. نخ‌های دیمون رشته‌های پس‌زمینه (رشته‌های سرویس) هستند که کارهایی را در پس‌زمینه انجام می‌دهند. این اصطلاح جالب اشاره ای به «شیطان ماکسول» است که می توانید در مقاله ویکی پدیا درباره « شیاطین » بیشتر بخوانید. همانطور که در مستندات ذکر شده است، JVM به اجرای برنامه (فرایند) تا زمانی که:
  • متد Runtime.exit فراخوانی نمی شود
  • همه رشته‌های غیر دیمون کار خود را کامل کردند (هم بدون خطا و هم به استثنای پرتاب شده)
از این رو جزئیات مهم: رشته های شبح را می توان در هر دستوری که اجرا می شود خاتمه داد. بنابراین، یکپارچگی داده های موجود در آنها تضمین نمی شود. بنابراین، موضوعات دیمون برای برخی از وظایف خدماتی مناسب هستند. به عنوان مثال، در جاوا رشته ای وجود دارد که وظیفه پردازش روش های نهایی یا موضوعات مربوط به جمع آوری زباله (GC) را بر عهده دارد. هر رشته به گروهی تعلق دارد ( ThreadGroup ). و گروه ها می توانند وارد یکدیگر شوند و سلسله مراتب یا ساختاری را تشکیل دهند.
public static void main(String []args){
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
گروه ها به شما امکان می دهند مدیریت جریان ها را ساده کنید و آنها را پیگیری کنید. علاوه بر گروه ها، thread ها کنترل کننده استثنای خود را دارند. بیایید به یک مثال نگاه کنیم:
public static void main(String []args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
تقسیم بر صفر باعث ایجاد خطایی می شود که توسط کنترل کننده تشخیص داده می شود. اگر خودتان هندلر را مشخص نکنید، پیاده‌سازی پیش‌فرض کنترلر کار خواهد کرد که پشته خطا را در StdError نمایش می‌دهد. شما می توانید در بررسی http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ ادامه مطلب را بخوانید . علاوه بر این، موضوع دارای اولویت است. شما می توانید در مورد اولویت ها بیشتر بخوانید مقاله " اولویت موضوع جاوا در چند رشته ای ".

ایجاد یک موضوع

همانطور که در مستندات گفته شد، ما 2 راه برای ایجاد یک موضوع داریم. اولین مورد این است که وارث خود را ایجاد کنید. مثلا:
public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");
        }
    }

    public static void main(String []args){
        Thread thread = new MyThread();
        thread.start();
    }
}
همانطور که می بینید، task در متد راه اندازی می شود runو موضوع در متد راه اندازی می شود start. آنها را نباید گیج کرد، زیرا ... اگر روش را runمستقیماً اجرا کنیم، هیچ موضوع جدیدی شروع نمی شود. این روشی است startکه از JVM می خواهد یک رشته جدید ایجاد کند. گزینه ای که دارای یک نسل از Thread است بد است زیرا ما Thread را در سلسله مراتب کلاس قرار می دهیم. دومین عیب این است که ما شروع به نقض اصل "مسئولیت انحصاری" SOLID می کنیم، زیرا کلاس ما به طور همزمان مسئول هم مدیریت thread و هم برای برخی وظایفی است که باید در این رشته انجام شود. کدام درسته؟ پاسخ در همان روشی است runکه ما لغو می کنیم:
public void run() {
	if (target != null) {
		target.run();
	}
}
در اینجا targetتعدادی وجود دارد java.lang.Runnableکه می توانیم هنگام ایجاد نمونه ای از کلاس به Thread ارسال کنیم. بنابراین، ما می توانیم این کار را انجام دهیم:
public class HelloWorld{
    public static void main(String []args){
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
همچنین Runnableاز جاوا 1.8 یک رابط کاربردی است. این به شما امکان می دهد کد وظیفه را برای رشته ها حتی زیباتر بنویسید:
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

جمع

بنابراین، امیدوارم از این داستان مشخص شود که جریان چیست، چگونه وجود دارد و چه عملیات اساسی را می توان با آنها انجام داد. در قسمت بعدی ، ارزش این را دارد که بفهمیم رشته ها چگونه با یکدیگر تعامل دارند و چرخه زندگی آنها چیست. #ویاچسلاو
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION