Java Multithreading: කෝ කට්ටිය Multithreading ගැන දැනගන්න SC Guide

Java Multithreading: කෝ කට්ටිය Multithreading ගැන දැනගන්න SC Guide

මචන්ලා, කොහොමද ඉතින්? අද අපි කතා කරන්න යන්නේ Java Programming වල ගොඩක්ම වැදගත්, හැබැයි ටිකක් විතර complicate වෙන්න පුළුවන් මාතෘකාවක් ගැන – ඒ තමයි Multithreading!

හිතන්නකෝ, ඔයාලා ලංකාවේ Busyම Bus Stand එකක ඉන්නවා කියලා. එකම වෙලාවක බස් ටිකක් පිටත් වෙනවා, මිනිස්සු ටිකට් ගන්නවා, තව කට්ටිය තේ බොනවා… මේ හැමදේම වෙන්නේ එකට. අපේ Computer වලත් වැඩ කරද්දි මෙහෙම තමයි. එකම වෙලාවක Task කිහිපයක් Run කරන්න වෙනවා. Web Browser එකක් Open කරලා, ඒකේ Music අහන ගමන්, Background එකේ File එකක් Download කරන එකට තමයි අපි Multithreading කියන්නේ.

සාමාන්‍යයෙන් අපේ Application එකක් Run වෙද්දී, ඒකේ තියෙන Instruction එකින් එක තමයි execute වෙන්නේ. හැබැයි එතකොට අපේ Application එක user ට නිතරම responsiveness නැතුව වගේ දැනෙන්න පුළුවන්. උදාහරණයක් විදිහට, ඔයාලා Game එකක් Play කරද්දි, ඒකේ Graphics Render වෙන ගමන්, Sound Play වෙන ගමන්, ඔයාලගේ Player ගේ Input ගන්න ගමන් වෙන්න ඕනේ. මේ හැමදේම එකට කරගන්න බැරි නම්, Game එක හිරවෙන්න (hang වෙන්න) ගන්නවා, එතකොට ආතල් එකක් නෑනේ!

ඔන්න ඔය වගේ වෙලාවට තමයි Multithreading අපිට උදව්වට එන්නේ. Multithreading කියන්නේ Program එකකට එකම වෙලාවක (simultaneously) Task කිහිපයක් Run කරන්න පුළුවන් හැකියාව. මේකෙන් අපේ Applications වල Performance එක වැඩි වෙනවා වගේම, User Experience එකත් ගොඩක් හොඳ වෙනවා.

එහෙනම් අපි යමු බලන්න මේ Multithreading කියන්නේ හරියටම මොකක්ද කියලා, Java වලදී මේක කොහොමද Use කරන්නේ කියලා.

Multithreading කියන්නේ මොකක්ද?

Multithreading කියන්නේ Program එකක එකම Project එකේ Task කිහිපයක් එකම වෙලාවක Run කරන එකට. මේක Concurrency කියන Concept එකේ කොටසක්. අපි Concurrency කියන එකයි Parallelism කියන එකයි ටිකක් වෙනස් කරලා තේරුම් ගමු.

  • Concurrency (සමගාමීත්වය): හිතන්න ඔයාලා එකම Cook (Chef) කෙනෙක් Cooking Pots කිහිපයක Dishes කිහිපයක් හදනවා කියලා. එක Cook කෙනෙක්ට එකම වෙලාවක Dishes කිහිපයක් Manage කරන්න පුළුවන්. ටිකක් මේකෙන්, ටිකක් අරකෙන් වගේ. මේක තමයි Concurrency කියන්නේ. ඒ කියන්නේ, එකම Processor Core එකක් Use කරගෙන, Tasks කිහිපයක් අතර ඉක්මනින් Switch වෙමින් වැඩ කරන එක.
  • Parallelism (සමාන්තරතාවය): දැන් හිතන්න, Cooking Pots කිහිපයක Dishes කිහිපයක් හදන්න Chefs ලා කිහිප දෙනෙක්ම ඉන්නවා කියලා. එතකොට එකම වෙලාවක Dishes කිහිපයක් එකපාරටම හදන්න පුළුවන්. මේක තමයි Parallelism කියන්නේ. ඒ කියන්නේ, Processor Cores කිහිපයක් Use කරගෙන, Tasks කිහිපයක් එකම වෙලාවක ඇත්තටම execute කරන එක.

ඉතින් Multithreading වලදී මේ Concurrencyයි Parallelism දෙකම achieve කරන්න පුළුවන්. අපේ Computer වල CPU වල Cores වැඩි වෙන තරමට Parallelism වැඩි කරගන්න පුළුවන්.

Thread එකක් කියන්නේ මොකක්ද?

සරලවම කිව්වොත්, Thread එකක් කියන්නේ Program එකක් ඇතුලේ තියෙන Execution Path එකක්. අපි Program එකක් Run කරද්දි, ඒක Process එකක් විදිහට තමයි Operating System එකේ Register වෙන්නේ. ඒ Process එක ඇතුලේ එකකට වඩා Threads තියෙන්න පුළුවන්.

Process එකක් සහ Thread එකක් අතර වෙනස

  • Process: මේක Operating System එකෙන් isolate වෙලා තියෙන independent program එකක්. හැම Process එකකටම තමන්ගේම memory space එකක් තියෙනවා. Process එකක් crash වුණොත්, අනිත් Process වලට බලපෑමක් වෙන්නේ නැහැ.
  • Thread: Thread එකක් කියන්නේ Process එකක් ඇතුලේ තියෙන lightweight sub-process එකක්. එකම Process එකක තියෙන Threads වලට ඒ Process එකේ memory space එක share කරගන්න පුළුවන්. මේක නිසා Threads අතර communication එක වේගවත් වෙනවා. හැබැයි, එක Thread එකක් crash වුණොත්, whole Process එකම crash වෙන්න පුළුවන්.

Thread Lifecycle එක

Thread එකක් Run වෙද්දි, ඒක Stage කිහිපයක් හරහා යනවා. මේක තමයි Thread Lifecycle එක:

  1. New: Thread එකක් Create කරපු ගමන්, ඒක මේ Stage එකේ තියෙන්නේ. තාම `start()` method එක Call කරලා නැහැ.
  2. Runnable: `start()` method එක Call කරාම Thread එක Runnable Stage එකට එනවා. මේකෙන් කියවෙන්නේ Thread එක දැන් Run වෙන්න සූදානම් කියලා. හැබැයි, CPU එකේ turn එක එනකම් සමහරවිට බලාගෙන ඉන්න වෙන්න පුළුවන්.
  3. Running: CPU එක Thread එකට Execute වෙන්න දුන්නාම, Thread එක Running Stage එකට එනවා. දැන් තමයි Thread එකේ `run()` method එකේ තියෙන code එක execute වෙන්නේ.
  4. Blocked/Waiting: සමහර වෙලාවට Thread එකකට යම්කිසි Resource එකක් (file එකක්, database එකක් වගේ) Release වෙනකම් බලාගෙන ඉන්න වෙනවා. එහෙම නැත්නම්, තව Thread එකක් ඉවර වෙනකම් බලාගෙන ඉන්න වෙනවා. එතකොට Thread එක මේ Stage එකට එනවා. උදාහරණ: I/O operation එකක් ඉවර වෙනකම් බලාගෙන ඉන්නවා, `synchronized` block එකක් Lock එක Release වෙනකම් බලාගෙන ඉන්නවා.
  5. Timed Waiting: මේකත් Waiting Stage එකක් වගේ තමයි. හැබැයි මේකේදී යම්කිසි time limit එකක් තියෙනවා. උදාහරණ: `sleep()` method එක Call කරද්දි හෝ `join()` method එක Call කරද්දි.
  6. Terminated: Thread එකේ `run()` method එකේ තියෙන code එක execute කරලා ඉවර වුණාම, Thread එක Terminated Stage එකට එනවා. එහෙම නැත්නම්, Unhandled Exception එකක් නිසා Thread එක Terminate වෙන්න පුළුවන්.

Threads හදන්නේ කොහොමද? (Creating Threads in Java)

Java වලදී Threads හදන්න ප්‍රධාන ක්‍රම දෙකක් තියෙනවා:

  1. `Thread` class එක Extend කිරීමෙන්.
  2. `Runnable` interface එක Implement කිරීමෙන්.

1. Thread class එක Extend කිරීමෙන් (Extending the Thread Class)

`java.lang.Thread` class එක extend කරලා අපිට Thread එකක් හදන්න පුළුවන්. මේකෙන් පස්සේ `run()` method එක Override කරලා අපිට කරන්න ඕනේ Task එක මේ `run()` method එක ඇතුලේ ලියන්න ඕනේ.

උදාහරණයක්:

class MyFirstThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": Count " + i);
            try {
                Thread.sleep(500); // Thread එක මිලි තත්පර 500ක් නවත්වනවා
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " interrupted.");
            }
        }
    }
}

public class ThreadExample1 {
    public static void main(String[] args) {
        MyFirstThread thread1 = new MyFirstThread();
        MyFirstThread thread2 = new MyFirstThread();

        thread1.setName("Thread-Alpha"); // Thread එකකට නමක් දාන්න පුළුවන්
        thread2.setName("Thread-Beta");

        thread1.start(); // Thread එක Run කරන්න start() method එක Call කරන්න ඕනේ
        thread2.start(); // Thread එක Run කරන්න start() method එක Call කරන්න ඕනේ

        System.out.println("Main thread finished.");
    }
}

මේ Code එක Run කරාම ඔයාලට පේනවා ඇති `Thread-Alpha` සහ `Thread-Beta` කියන Threads දෙක එකට Run වෙනවා. Output එකේ Order එක හැම වෙලාවෙම එක වගේ වෙන්නේ නැහැ, මොකද Operating System එක තීරණය කරනවා Thread එකට CPU time එක දෙන්නේ කොහොමද කියලා.

වැදගත්: `start()` method එක Call කරන්නේ Thread එක අලුත් Execution Path එකක Run කරන්න. `run()` method එක කෙලින්ම Call කරොත්, Thread එක Run වෙන්නේ Main Thread එකේම තමයි, Multithreading වෙන්නේ නැහැ.

2. Runnable interface එක Implement කිරීමෙන් (Implementing the Runnable Interface)

මේක තමයි Threads හදන්න හොඳම සහ Preferred ක්‍රමය. මොකද Java වල Class එකකට extend කරන්න පුළුවන් Single Class එකක් විතරයි. හැබැයි Implement කරන්න පුළුවන් Interfaces ඕන තරම්. ඒ නිසා `Thread` class එක Extend කරනවාට වඩා `Runnable` interface එක Implement කරන එකෙන් code එකේ Flexibility එක වැඩි වෙනවා.

class MyRunnableThread implements Runnable {
    private String threadName;

    public MyRunnableThread(String name) {
        this.threadName = name;
    }

    @Override
    public void run() {
        System.out.println(threadName + " starting.");
        for (int i = 0; i < 5; i++) {
            System.out.println(threadName + ": Count " + i);
            try {
                Thread.sleep(700); // ටිකක් වැඩි වෙලාවක් නවත්වනවා
            } catch (InterruptedException e) {
                System.out.println(threadName + " interrupted.");
            }
        }
        System.out.println(threadName + " finishing.");
    }
}

public class ThreadExample2 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnableThread("Runnable-Thread-X"));
        Thread thread2 = new Thread(new MyRunnableThread("Runnable-Thread-Y"));

        thread1.start();
        thread2.start();

        System.out.println("Main thread finished.");
    }
}

මේකෙදි අපි `MyRunnableThread` class එකෙන් `Runnable` interface එක implement කරනවා. ඊට පස්සේ `Thread` class එකේ Constructor එකට මේ `MyRunnableThread` Object එක Parameter එකක් විදිහට දෙනවා. අනිත් ඔක්කොම `Thread` class එක Extend කරනවා වගේමයි.

Multithreading වලදී එන ගැටළු (Common Problems in Multithreading)

Multithreading වලින් අපේ Application වල performance එක වැඩි කරගන්න පුළුවන් වුණාට, මේකේදී අලුත් ගැටළු ටිකකුත් ඇති වෙන්න පුළුවන්. ඒක හරියට Busy Road එකක Driving කරනවා වගේ තමයි. වාසි තියෙනවා වගේම, Traffic Jam, අනතුරු වගේ අවදානමකුත් තියෙනවා. ප්‍රධාන ගැටළු දෙකක් තමයි:

  1. Race Condition: Threads කිහිපයක් එකම Shared Resource එකකට (variable, object වගේ) එකම වෙලාවක access කරන්න ගිහින්, අනපේක්ෂිත විදිහට data එක වෙනස් වීම. මේකෙන් incorrect results එන්න පුළුවන්. මේක විසඳන්න `synchronization` techniques (උදා: `synchronized` keyword, Locks) භාවිතා කරන්න ඕනේ.
  2. Deadlock: Threads දෙකක් හෝ වැඩි ගණනක් එකිනෙකාට රඳා පවතින Lock එකක් Release වෙනකම් බලාගෙන ඉඳිද්දී ඇති වෙන තත්වය. මේකෙන් Application එක හිරවෙන්න (freeze වෙන්න) පුළුවන්. මේකත් ගොඩක්ම පරිස්සමෙන් Handle කරන්න ඕනේ.

මේවා ගැන අපි අනාගත Blog Posts වලදී වැඩිදුරට කතා කරමු.

අවසන් වශයෙන්

ඉතින් මචන්ලා, අද අපි Multithreading කියන්නේ මොකක්ද, ඒකේ තියෙන වාසි මොනවද, Thread එකක් කියන්නේ මොකක්ද, Thread Lifecycle එක කොහොමද, වගේම Java වලදී Threads හදන්නේ කොහොමද කියලා කතා කරා. `Thread` class එක Extend කරන එකට වඩා `Runnable` interface එක Implement කරන එක හොඳයි කියලත් දැන් ඔයාලා දන්නවා. වගේම Multithreading වලදී එන පොදු ගැටළු දෙකකුත් අපි කතා කරා.

මේ Concepts ටික තේරුම් ගන්න ටිකක් අමාරු වෙන්න පුළුවන්, හැබැයි Practice එකෙන් තේරුම් ගන්න පුළුවන්. ඒ නිසා අනිවාර්යයෙන්ම මේ Code Examples ටික ඔයාලගේ Computer එකේ Try කරලා බලන්න. Output එක Analyze කරන්න. එකම Code එක දෙපාරක් තුන්පාරක් Run කරලා, Output එක වෙනස් වෙන හැටි බලන්න.

මේ Blog Post එක ගැන ඔයාලගේ අදහස්, ප්‍රශ්න පහලින් Comment Section එකේ දාන්න. තව මොනවා ගැනද ඔයාලට දැනගන්න ඕනේ කියලා කියන්නත් අමතක කරන්න එපා. අපි ඊළඟ Blog Post එකෙන් හම්බවෙමු! ආයෙත් හම්බවෙමු!