簡體   English   中英

在 2 個線程的幫助下打印自然序列(1 個打印偶數,2'nd 打印奇數)

[英]Print Natural Sequence with help of 2 threads(1 is printing even and 2'nd is printing odd)

我已經厭倦了這個問題,最終我產生了一些疑問。 請幫幫我

懷疑:如果任何線程處於等待狀態,並且沒有其他線程通知該線程,那么它永遠不會結束嗎? 即使在使用等待(長毫秒)之后。

對於代碼:我的要求來自代碼(請參閱我的代碼):

a : 應該打印“Even Thread Finish”和“Odd Thread Finish”(訂單不是 imp ,但必須同時打印)

b: 同樣在主函數中應該打印“退出主線程”

實際發生的情況:經過多次運行,在某些情況下,它會打印“Even Thread Finish”,然后掛在這里,反之亦然。 在某些情況下,它會同時打印兩者。

它也從不打印“退出主線程”。

那么如何修改代碼,所以它必須打印所有 3 條語句。(當然“退出 Main..”最后,因為我使用 join 作為 main。)

簡而言之:主要開始-> t1 開始-> t2 開始,然后我需要t2/t1 完成-> 主要完成。

請幫我解決這個問題

這是我的代碼:

import javax.sql.CommonDataSource;

public class ThreadTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Share commonObj = new Share();

        Thread even = new Thread(new EvenThread(commonObj));

        Thread odd = new Thread(new OddThread(commonObj));

        even.start();

        odd.start();

        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("Exit Main Thread");

    }

}

class EvenThread implements Runnable {

    private Share commShare;
    public EvenThread(Share obj) {
        // TODO Auto-generated constructor stub
        this.commShare = obj;
    }

    private int number = 2;

    public void run() {
        System.out.println("Even Thread start");
        while (number <= 50) {
            if (commShare.flag == true) {
                System.out.println("Even Thread" + number);
                number += 2;
                commShare.flag = false;
                synchronized(commShare) {
                    try {
                        commShare.notify();
                        commShare.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    commShare.notify();
                }

            } else {
                synchronized(commShare) {
                    try {
                        commShare.notify();
                        commShare.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    commShare.notify();
                }
            }

        }

        System.out.println("Even Thread Finish");
    }
}


class OddThread implements Runnable {

    private int number = 1;
    private Share commShare;


    public OddThread(Share obj) {
        // TODO Auto-generated constructor stub
        this.commShare = obj;
    }



    public void run() {
        System.out.println("Odd Thread start");
        while (number <= 50) {
            if (commShare.flag == false) {
                System.out.println("Odd Thread :" + number);
                number += 2;
                commShare.flag = true;
                synchronized(commShare) {
                    try {
                        commShare.notify();
                        commShare.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    commShare.notify();
                }
            }
        }
        System.out.println("Odd Thread Finish");
    }
}

class Share {

    Share sharedObj;
    public boolean flag = false;
}

雖然這不是您問題的確切答案,但此實現是您問題的替代方案。

public class EvenOddThreads {
    public static void main(String[] args) {
        Thread odd = new Thread(new OddThread(), "oddThread");

        Thread even = new Thread(new EvenThread(), "Even Thread");

        odd.start();
        even.start();
        try {
            odd.join();
            even.join();
            System.out.println("Main thread exited");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
class OddThread implements Runnable{
    public void run() {
        synchronized (CommonUtil.mLock) {
            System.out.println(Thread.currentThread().getName()+"---> job starting");
            int i = 1;
            while(i<50){
                System.out.print(i + "\t");
                i = i + 2;
                CommonUtil.mLock.notify();
                try {
                    CommonUtil.mLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("OddThread---> job completed");
            CommonUtil.mLock.notify();

        }
    }
}

class EvenThread implements Runnable{
    @Override
    public void run() {
        synchronized (CommonUtil.mLock) {
            System.out.println(Thread.currentThread().getName()+"---> job started");
            int i =2;
            while(i<50){
                System.out.print(i + "\t");
                i = i+2;
                CommonUtil.mLock.notify();
                try {
                    CommonUtil.mLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("EvenThread---> job completed");
            CommonUtil.mLock.notify();
        }
    }
}

class CommonUtil{
    static final Object mLock= new Object();
}

輸出:

oddThread---> job starting
1   Even Thread---> job started
2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  EvenThread---> job completed
OddThread---> job completed
Main thread exited

好吧,我花了三個小時閱讀Java 同步教程(非常好的教程),然后閱讀了有關wait、notify 和 notifyAll的更多信息,最后我得到了使用 N 個線程從 A 到 B 計數的程序,將 N 設置為2 你有奇數和偶數。

粘貼箱

此外,我的程序沒有任何注釋,因此在嘗試理解此代碼之前,請務必閱讀教程。

這可能是關於線程和鎖監視器的練習,但沒有任何並行可以給您帶來好處。

在您的代碼中,當線程 1(OddThread 或 EvenThread)結束其工作並打印出“Odd Thread Finish”(或“Even Thread Finish”)時,另一個線程 2 正在等待一個永遠不會發生的 notify() 或 notifyAll()因為第一次結束了。

您必須更改 EvenThread 和 OddThread,在 while 循環之后添加一個同步塊,並在 commShare 上調用通知。 我刪除了第二個 if 分支,因為通過這種方式,您不會繼續檢查 while 條件,但很快就會等待 commShare。

class EvenThread implements Runnable {
    private Share commShare;
    private int number = 2;

    public EvenThread(Share obj) {
        this.commShare = obj;
    }
    public void run() {
        System.out.println("Even Thread start");
        while (number <= 50) {
            synchronized (commShare) {
                if (commShare.flag) {
                    System.out.println("Even Thread:" + number);
                    number += 2;
                    commShare.flag = false;
                }
                commShare.notify();
                try {
                    commShare.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        synchronized (commShare) {
            commShare.notify();
            System.out.println("Even Thread Finish");
        }
    }
}

class OddThread implements Runnable {
    private int number = 1;
    private Share commShare;

    public OddThread(Share obj) {
        this.commShare = obj;
    }
    public void run() {
        System.out.println("Odd Thread start");
        while (number <= 50) {
            synchronized (commShare) {
                if (!commShare.flag) {
                    System.out.println("Odd Thread: " + number);
                    number += 2;
                    commShare.flag = true;
                }
                commShare.notify();
                try {
                    commShare.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }
        synchronized (commShare) {
            commShare.notify();
            System.out.println("Odd Thread Finish");
        }
    }

最后,總的來說,您必須為您啟動的每個線程加入。 確定 Thread.currentThread() 只返回您的線程之一嗎? 我們已經啟動了兩個線程,我們應該加入這些線程。

try {
      even.join();
      odd.join();
} catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
}

它也從不打印“退出主線程”。

那是因為可能是因為您的線程正在等待某人notify()的鎖,但由於錯過了信號或沒有人向他們發出信號,他們永遠不會退出等待狀態。 為此,最好的解決方案是使用:

public final void wait(long timeout)
                throws InterruptedException

導致當前線程等待,直到另一個線程為此對象調用notify()方法或notifyAll()方法,或者指定的時間量已經過去。

此重載方法將等待其他線程通知特定時間,然后在超時發生時返回。 因此,在丟失信號的情況下,線程仍將恢復其工作。

注意:從等待狀態返回后,始終再次檢查PRE-CONDITION ,因為它可能是Spurious Wakeup

這是我一段時間以前編寫的程序風格。

import java.util.concurrent.atomic.AtomicInteger;


public class Main {

    private static int range = 10;
    private static volatile AtomicInteger present = new AtomicInteger(0);
    private static Object lock = new Object();

    public static void main(String[] args) {
        new Thread(new OddRunnable()).start();
        new Thread(new EvenRunnable()).start();
    }

    static class OddRunnable implements Runnable{

        @Override
        public void run() {
            while(present.get() <= range){
                if((present.get() % 2) != 0){
                    System.out.println(present.get());
                    present.incrementAndGet();
                    synchronized (lock) {
                        lock.notifyAll();
                    }
                }else{
                    synchronized (lock) {
                        try {
                            lock.wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            break;
                        }
                    }
                }
            }
        }
    }

    static class EvenRunnable implements Runnable{

        @Override
        public void run() {
            while(present.get() <= range){
                if((present.get() % 2) == 0){
                    System.out.println(present.get());
                    present.incrementAndGet();
                    synchronized (lock) {
                        lock.notifyAll();
                    }
                }else{
                    synchronized (lock) {
                        try {
                            lock.wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            break;
                        }
                    }
                }
            }
        }
    }
}

查看解決方案,我保留了一個lock ,用於通知偶數或奇數線程的機會。 如果偶數線程發現當前的數字不是偶數,它就等待lock並希望奇數線程在打印該奇數時通知它。 同樣,它也適用於奇數線程。

我並不是說這是最好的解決方案,但這是第一次嘗試的結果,其他一些選擇也是可能的。

另外我想指出,這個問題雖然作為一種實踐是好的,但請記住,你沒有在那里做任何平行的事情。

我不會投票支持使用wait()notify(). 您可以通過更復雜的工具(如semaphorecountDownLatchCyclicBarrier完成waitnotify操作。 您可以在著名書籍 Effective java 的第 69 項中找到此建議, 更喜歡並發實用程序等待和通知

即使在這種情況下我們根本不需要這些東西,我們可以通過一個簡單的volatile boolean變量來實現這個功能。 對於停止線程,最好的方法是使用interrupt 在一定的時間或一些預定義的條件之后,我們可以中斷線程。 請附上我的實現:

用於打印偶數的線程 1:

public class MyRunnable1 implements Runnable
{
    public static volatile boolean isRun = false;
    private int k = 0 ;
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){
            if(isRun){
                System.out.println(k);
                k+=2;
                isRun=false;
                MyRunnable2.isRun=true;
            }
        }
    }
}

用於打印偶數的線程 2:

public class MyRunnable2 implements Runnable{
    public static volatile boolean isRun = false;
    private int k = 1 ;
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){
            if(isRun){
                System.out.println(k);
                k+=2;
                isRun=false;
                MyRunnable1.isRun=true;
            }
        }
    }
}

現在驅動上述線程的主要方法

public class MyMain{
    public static void main(String[] args) throws InterruptedException{
        Thread t1 = new Thread(new MyRunnable1());
        Thread t2 = new Thread(new MyRunnable2());
        MyRunnable1.isRun=true;
        t1.start();
        t2.start();
        Thread.currentThread().sleep(1000);
        t1.interrupt();
        t2.interrupt();
    }
}

可能有些地方你需要稍微改變一下,這只是一個骨架實現。 希望它有所幫助,如果您需要其他東西,請告訴我。

public class PrintNumbers {

    public static class Condition {
        private boolean start = false;
        public boolean getStart() {
            return start;
        }

        public void setStart(boolean start) {
            this.start = start;
        }
    }

    public static void main(String[] args) {

        final Object lock = new Object();
        // condition used to start the odd number thread first
        final Condition condition = new Condition();

        Thread oddThread = new Thread(new Runnable() {
            public void run() {
                synchronized (lock) {
                    for (int i = 1; i <= 10; i = i + 2) { //For simplicity assume  only printing till 10;
                        System.out.println(i);
                        //update condition value to signify that odd number thread has printed first
                        if (condition.getStart() == false) {
                            condition.setStart(true);
                        }
                        lock.notify();
                        try {
                            if (i + 2 <= 10) { 
                                lock.wait(); //if more numbers to print, wait;
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        });

        Thread evenThread = new Thread(new Runnable() {
            public void run() {
                synchronized (lock) {
                    for (int i = 2; i <= 10; i = i + 2) { //For simplicity assume only printing till 10;
                        // if thread with odd number has not printed first, then wait
                        while (condition.getStart() == false) {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println(i);
                        lock.notify();
                        try {
                            if (i + 2 <= 10) { //if more numbers to print, wait;
                                lock.wait();
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        });

        oddThread.start();
        evenThread.start();

    }

}

我使用 25 個線程的 ReentrantLock 做到了。 一個線程打印一個數字,它會通知其他。

public class ReentrantLockHolder 
{
    private Lock lock;

    private Condition condition;

    public ReentrantLockHolder(Lock lock )
    {
        this.lock=lock;
        this.condition=this.lock.newCondition();
    }

    public Lock getLock() {
        return lock;
    }

    public void setLock(Lock lock) {
        this.lock = lock;
    }

    public Condition getCondition() {
        return condition;
    }

    public void setCondition(Condition condition) {
        this.condition = condition;
    }
}
public class PrintThreadUsingReentrantLock implements Runnable
{
    private ReentrantLockHolder currHolder;

    private ReentrantLockHolder nextHolder;

    private PrintWriter writer;

    private static int i=0;

    public PrintThreadUsingReentrantLock(ReentrantLockHolder currHolder, ReentrantLockHolder nextHolder ,PrintWriter writer)
    {
        this.currHolder=currHolder;
        this.nextHolder=nextHolder;
        this.writer=writer;
    }

    @Override
    public void run() 
    {
        while (true) 
        {
            writer.println(Thread.currentThread().getName()+ " "+ ++i);

            try{
                nextHolder.getLock().lock();
                nextHolder.getCondition().signal();
            }finally{
                nextHolder.getLock().unlock();  
            }

            try {
                currHolder.getLock().lock();
                currHolder.getCondition().await();
            }catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
            finally{
                currHolder.getLock().unlock();
            }
        }
    }
}
public static void main(String[] args) 
    {
        PrintWriter printWriter =null;
        try {
                printWriter=new PrintWriter(new FileOutputStream(new File("D://myFile.txt")));
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        ReentrantLockHolder obj[]=new ReentrantLockHolder[25];
        for(int i=0;i<25;i++)
        {
            obj[i]=new ReentrantLockHolder(new ReentrantLock());
        }

        for(int i=0;i<25;i++)
        {
            Thread t1=new Thread(new PrintThreadUsingReentrantLock(obj[i], obj[i+1 == 25 ? 0 : i+1],printWriter ),"T"+i );
            t1.start();
        }
    }

我嘗試了類似的東西,其中線程 1 打印奇數,線程 2 以正確的順序打印偶數,並且當打印結束時,將打印您建議的所需消息。 請看一下這段代碼

package practice;


class Test {

  private static boolean oddFlag = true;
  int count = 1;

  private void oddPrinter() {
    synchronized (this) {
      while(true) {
        try {
          if(count < 10) {
            if(oddFlag) {
              Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + ": " + count++);
              oddFlag = !oddFlag;
              notifyAll();
            }
            else {
              wait();
            }
          }
          else {
            System.out.println("Odd Thread finished");
            notify();
            break;
          }
        }
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  private void evenPrinter() {
    synchronized (this) {
      while (true) {
        try {
          if(count < 10) {
            if(!oddFlag) {
              Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + ": " + count++);
              oddFlag = !oddFlag;
              notify();
            }
            else {
              wait();
            }
          }
          else {
            System.out.println("Even Thread finished");
            notify();
            break;
          }
        }
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }


  public static void main(String[] args) throws InterruptedException{
    final Test test = new Test();

    Thread t1 = new Thread(new Runnable() {
      public void run() {
        test.oddPrinter();
      }
    }, "Thread 1");

    Thread t2 = new Thread(new Runnable() {
      public void run() {
        test.evenPrinter();
      }
    }, "Thread 2");

    t1.start();
    t2.start();

    t1.join();
    t2.join();

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

public class Interview2 {

public static void main(String[] args) {
    Obj obj = new Obj();

    Runnable evenThread = ()-> {
        synchronized (obj) {
            for(int i=2;i<=50;i+=2) {
                while(!obj.printEven) {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }                   
                }
                System.out.println(i);
                obj.printEven = false;
                obj.notify();
            }
        }           
    };
    Runnable oddThread = ()-> {
        synchronized (obj) {
            for(int i=1;i<=49;i+=2) {
                while(obj.printEven) {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }                   
                }
                System.out.println(i);
                obj.printEven = true;
                obj.notify();
            }
        }
    };      
    new Thread(evenThread).start();
    new Thread(oddThread).start();
   }
}
class Obj {
  boolean printEven;
}

這是非常通用的解決方案。 它使用信號量在線程之間進行信號傳遞。 這是 N 個線程依次依次打印 M 個自然數的通用解決方案。 也就是說,如果我們有 3 個線程並且我們想要打印 7 個自然數,輸出將是:

主題 1 : 1

主題 2 : 2

主題 3 : 3

主題 1 : 4

主題 2 : 5

主題 3 : 6

主題 1 : 7

import java.util.concurrent.Semaphore;

/*
 * Logic is based on simple idea
 * each thread should wait for previous thread and then notify next thread in circular fashion
* There is no locking required
* Semaphores will do the signaling work among threads.
*/

public class NThreadsMNaturalNumbers {

private static volatile int nextNumberToPrint = 1;
private static int MaxNumberToPrint;

public static void main(String[] args) {

    int numberOfThreads = 2;
    MaxNumberToPrint = 50;

    Semaphore s[] = new Semaphore[numberOfThreads];

    // initialize Semaphores
    for (int i = 0; i < numberOfThreads; i++) {
        s[i] = new Semaphore(0);
    }

    // Create threads and initialize which thread they wait for and notify to
    for (int i = 1; i <= numberOfThreads; i++) {
        new Thread(new NumberPrinter("Thread " + i, s[i - 1], s[i % numberOfThreads])).start();
    }
    s[0].release();// So that First Thread can start Processing
}

private static class NumberPrinter implements Runnable {

    private final Semaphore waitFor;
    private final Semaphore notifyTo;
    private final String name;

    public NumberPrinter(String name, Semaphore waitFor, Semaphore notifyTo) {
        this.waitFor = waitFor;
        this.notifyTo = notifyTo;
        this.name = name;
    }

    @Override
    public void run() {

        while (NThreadsMNaturalNumbers.nextNumberToPrint <= NThreadsMNaturalNumbers.MaxNumberToPrint) {
            waitFor.acquireUninterruptibly();
            if (NThreadsMNaturalNumbers.nextNumberToPrint <= NThreadsMNaturalNumbers.MaxNumberToPrint) {
                System.out.println(name + " : " + NThreadsMNaturalNumbers.nextNumberToPrint++);
                notifyTo.release();
            }

        }
        notifyTo.release();
    }

}

}

這個類打印偶數:

public class EvenThreadDetails extends Thread{

    int countNumber;
     public EvenThreadDetails(int countNumber) {
        this.countNumber=countNumber;
    }
    @Override
    public void run()
    {
        for (int i = 0; i < countNumber; i++) {
            if(i%2==0)
            {
                System.out.println("Even Number :"+i);
            }
            try {
                Thread.sleep(2);
            } catch (InterruptedException ex) {
                // code to resume or terminate...
            }
        }
    }
}

    

這個類打印奇數:

public class OddThreadDetails extends Thread {

    int countNumber;
     public OddThreadDetails(int countNumber) {
        this.countNumber=countNumber;
    }
    @Override
    public void run()
    {
        for (int i = 0; i < countNumber; i++) {
            if(i%2!=0)
            {
                System.out.println("Odd Number :"+i);
            }
            try {
                Thread.sleep(2);
            } catch (InterruptedException ex) {
                // code to resume or terminate...
            }       
        }
    }
}

這是主類:

public class EvenOddDemo {

    public static void main(String[] args) throws InterruptedException
    {
        Thread eventhread= new EvenThreadDetails(100);
        Thread oddhread=new OddThreadDetails(100);
        eventhread.start();
        oddhread.start();   
    }
}
I have done it this way and its working...

class Printoddeven{

public synchronized void print(String msg){
    try {
        if(msg.equals("Even"))
        {
        for(int i=0;i<=10;i+=2){
            System.out.println(msg+" "+i);
            Thread.sleep(2000);
            notify();
            wait();
        }
        }

        else{
            for(int i=1;i<=10;i+=2){
                System.out.println(msg+" "+i);
                Thread.sleep(2000);
                notify();
                wait();
            }
        }

    } catch (Exception e) {

        e.printStackTrace();
    }
}

}

class PrintOdd extends Thread{
Printoddeven oddeven;
public PrintOdd(Printoddeven oddeven){
    this.oddeven=oddeven;
}

public void run(){
    oddeven.print("ODD");

}

}

class PrintEven extends Thread{
Printoddeven oddeven;
public PrintEven(Printoddeven oddeven){
    this.oddeven=oddeven;
}

public void run(){
    oddeven.print("Even");

}

}



public class mainclass 
{

public static void main(String[] args) 
{

    Printoddeven obj = new Printoddeven();//only one object  
    PrintEven t1=new PrintEven(obj);  
    PrintOdd t2=new PrintOdd(obj);  
    t1.start();  
    t2.start();  

}
}
public class Driver {
    static Object lock = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            public void run() {

                for (int itr = 1; itr < 51; itr = itr + 2) {
                    synchronized (lock) {
                        System.out.print(" " + itr);
                        try {
                            lock.notify();
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                System.out.println("\nEven Thread Finish ");
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {

                for (int itr = 2; itr < 51; itr = itr + 2) {
                    synchronized (lock) {
                        System.out.print(" " + itr);
                        try {
                            lock.notify();
                            if(itr==50)
                                break;
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                System.out.println("\nOdd Thread Finish ");
            }
        });
        try {
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println("Exit Main Thread");
        } catch (Exception e) {

        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM