简体   繁体   中英

i want to test java multithread ,but only one thread run

public class ThreadText
{
    public static void main(String[] args)
    {
        windows w=new windows();
        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3=new Thread(w);
        Thread t4=new Thread(w);/*four threads*/
        t1.start();
        t2.start();
        t3.start();
        t4.start();
     }
}

i want to use four windows to sale 1000 tickets

class windows implements Runnable
    {
    int tickets=1000;
    public void run()
    {
    synchronized(this)
    {
        while(tickets>0)
        {
            System.out.println(Thread.currentThread().getName()+" is saling"+tickets);
            tickets--;
        }
    }/*i think problem is here*/    
    }
}

when i don't use synchronized,all threads run,but results are wrong. some tickets number are same.the correct result is that every window gets different ticket number.

Thanks

When you synchronize the entire loop, the first thread will get the lock and will count down to 0. The other 3 threads will wait until first thread releases the lock, at which point there's nothing left for them to do. In effect, the result is single-threaded.

To work correctly, you need to synchronize inside the loop, so the other threads can work too.

However, the first thread may still be too fast , so that it ends up doing all the work. To better simulate what you want, add a delay outside the synchronize block (making a sale takes time, you know) . Added minimum delay of 1ms in the code below.

And finally, Java naming convention is for class names to start with an Uppercase letter, so it should be named Windows .

class Windows implements Runnable {
    private int tickets = 1000;
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        for (;;) { // loop forever
            synchronized (this){
                if (this.tickets == 0)
                    break;
                System.out.println(threadName + " is saling " + this.tickets);
                this.tickets--;
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                break;
            }
        }
    }
}

OUTPUT

Thread-0 is saling 1000
Thread-2 is saling 999
Thread-3 is saling 998
Thread-1 is saling 997
Thread-0 is saling 996
Thread-1 is saling 995
Thread-2 is saling 994
Thread-3 is saling 993
Thread-0 is saling 992
Thread-1 is saling 991
Thread-3 is saling 990
Thread-2 is saling 989
        . . .
Thread-2 is saling 11
Thread-1 is saling 10
Thread-0 is saling 9
Thread-3 is saling 8
Thread-0 is saling 7
Thread-1 is saling 6
Thread-2 is saling 5
Thread-1 is saling 4
Thread-0 is saling 3
Thread-3 is saling 2
Thread-2 is saling 1

What you are experiencing is known as a race condition . That means due to the order of threads operating, you can get two threads thinking they decremented something but only one did, etc.

To address this, you have two options:

  • synchronized -- which is pretty heavy handed, and unless you put it at the smallest critical section is going to be too much.
  • AtomicInteger -- which makes sure that the integer is incremented or decremented safely across threads.

To fix your example and still use synchronized , your code should look like this:

class windows implements Runnable
{
    int tickets=1000;
    public void run()
    {
        while(tickets>0)
        {
            synchronized(this)
            {
                System.out.println(Thread.currentThread().getName()+" is saling"+tickets);
                tickets--;
            }
         }
     }
 }

Of course, the down side to that is you have to remember to synchronize all access to the variable.

Using AtomicInteger the process is much easier to control:

class windows implements Runnable
{
    AtomicInteger tickets = new AtomicInteger(1000);
    public void run()
    {
        while(tickets.get() > 0)
        {
            System.out.println(Thread.currentThread().getName()
                + " is saling" + tickets.decrementAndGet());
         }
     }
 }

You do need to synchronize due to the tickets member being accessed by multiple threads. But because you are basing the run method on while tickets > 0, only 1 thread will ever run until the end. One solution would be to tie the execution of the thread to another variable and have the run method just decrement tickets once, then sleep so other threads can have a chance to run. For example, if you refactor your windows class as follows, then it will do what I think you want it to do.

class windows implements Runnable {
    volatile boolean finished = false;
    int tickets = 1000;

    public void run() {
        while(!finished) {
            synchronized(this) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName()+" is selling " + tickets);
                    tickets--;

                    if (tickets <= 0) {
                        finished = true;
                    }
                }
            }
            try {
                Thread.sleep(10);
            }
            catch(InterruptedException ex) {
            }
        }
    }
}

From your code I understand that you are trying to replicate a Sales Window in Real Life for the Tickets. Analogy (Look at the Image Link at the bottom of answer): A place for sales of Tickets (Say, concert Tickets). This place has multiple Sections/Windows from where the exchange of money happens and the concert ticket is provided. Behind the Window there is a person that actually takes the money and gives u the ticket. Since in your example there is nothing related to money, so this is just a one sided exchange. Basically the person behind the window gives the ticket when ever a customer comes (Customer coming is like thread getting executed in your example).

There are only limited finite number of Ticket, each having a unique ticket number between 1 to 1000.

Your code has same instance of Runnable Window. And this same instance is provided for execution to all threads (All different Instances of Thread). So your Real Life Design becomes like: That the same person will give you the tickets from Window 1, 2, 3 etc. But at one time only one window can be taken care by this person. So making your code effectively to run as Single Processer/Core code but poorer performance than the sequential code as the thread switching (if occurred) will have more overheads.

Now lets say we had multiple sales person - One for each window. Then the question becomes of NOT selling the same ticket by different sales person (Race Condition).

For this all the sales person have to take a ticket from a box one at a time. IE the part of getting the ticket is the only part that needs to be synchronized. This would be best achieved by Atomic Integers. Other answers have more than required chunk of code with in their synchronized blocks. This part has been answered (as per me) by @Berin Loritsch reply.

class TicketSeller /* Not windows*/ implements Runnable {
        AtomicInteger tickets = new AtomicInteger(1000);

        public void run() {
            while (tickets.get() > 0) {
                System.out.println(Thread.currentThread().getName()
                        + " is saling" + tickets.decrementAndGet());
            }
        }
    }

public class ThreadText {
    public static void main(String[] args) {
        Thread t1 = new Thread(new TicketSeller());
        Thread t2 = new Thread(new TicketSeller());
        Thread t3 = new Thread(new TicketSeller());
        Thread t4 = new Thread(new TicketSeller());                        
        /*four threads, four windows from where the ticket can be provided by 4 
          different TicketSellers*/
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
 }

With this when a different thread comes into execution or multiple threads run in parallel the thread is not put to unnecessary sleep (And sleep doesn't even confirm avoiding of the race condition, by the way) and is blocked from execution for only a limited area of the code, ie tickets.get().

Of course you can go a few steps ahead an optimize this even more. But that would require a lot more deeper understanding and complex thinking. Eg Sales person is allocated a certain number of tickets. And ticket shuffling happens so that to optimize that maximum number of tickets are sold in quickest execution time by something managing the tickets that each SalePerson has.

Ticket Sale Window Example: https://www.google.com/imgres?imgurl=https%3A%2F%2Fimage.shutterstock.com%2Fimage-photo%2Fthree-windows-stadium-ticket-offices-260nw-1776188525.jpg&imgrefurl=https%3A%2F%2Fwww.shutterstock.com%2Fsearch%2Ftickets%2Bwindow&tbnid=BW8bA0aSqJTxaM&vet=12ahUKEwiVvvrmp47zAhU6g0sFHW8TCekQMygEegUIARC9AQ..i&docid=QQwLjL01dRWDoM&w=390&h=280&q=Ticket%20Sale%20Window&ved=2ahUKEwiVvvrmp47zAhU6g0sFHW8TCekQMygEegUIARC9AQ

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM