In reply to mlsxdx:
For case 1 and case 2, you cannot rely on the ordering of the processes started by a fork/join block - these are race conditions. It just so happens that your simulator chooses to execute them in the order they appear in the source code. If you need a predictable ordering, then you need to use a begin/end instead of fork/join.
For case 1, the consumer executes first before any producer has done a put() into the mailbox, so checker_data.num() == 0. For case 2, the consumer executes last after all producers have done put()s into the mailbox, so checker_data.num() == 3.
As I said above, the get() is your blocking delay, there is no need for a #1.
task automatic consumer();
bit [7:0] data;
while(1) begin // I would use 'forever begin'
checker_data.get(data); // blocks until the get() succeeds
$display($time,,"Got data:%0h",data);
end
endtask
Also, use $urandom instead of $random. You get stable seeding of each process regardless of execution ordering of the threads.