I cannot understand why a thread is able to call a method of an object that is declered in an another thread which is in a while true loop. From my basic knowledge if a thread is in a while true loop you cannot interact with it and therefore even with the object declered in this thread.
Thank you in advice.
this is the main class
/**
* main
*/
public class mainClass {
public static void main(String[] args) {
whatTime wt = new whatTime();
threadA ta = new threadA(wt);
ta.start();
while (true) {
}
}
}
this is the thread A class
/**
* threadA
*/
public class threadA extends Thread {
private whatTime wt;
public threadA(whatTime wt) {
System.out.println("threadA() constructor");
this.wt = wt;
}
public void run() {
while (true) {
//every 10s
try {
Thread.sleep(10000);
System.out.println("threadA: " wt.getTime());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
and this is the object that i use
public class whatTime {
public whatTime() {
System.out.println("whatTime() constructor");
}
public long getTime() {
return System.currentTimeMillis();
}
}
CodePudding user response:
I cannot understand why a thread is able to call a method of an object that is declered in an another thread
I think you misunderstand.
whatTime wt = new whatTime();
This does 2 completely different things.
In the local space (which is in the stack, and indeed, cannot be interacted with by other threads, whatsoever - that stack is instantly reused by whatever happens immediately after this method returns, so if any other thread dares to try, you're in big trouble) - it declares a reference variable. That's generally a 64-bit value that tells the JVM where to find an object in the heap. It is not the instance of whatTime itself that is stored in wt
! - it is merely a reference to it. Think: "An page in an address book with the address of a house on it", not: "A house". an instanceof whatTime
is the house, wt
is the address book page.
Other threads cannot interact with the address book page - at best, you can make a copy of it, and hand the copy to another thread. But they can take their copy of the address book page, drive over there, toss a brick through the window. If later that day you also drive over there you will - see the damaged window.
new WhatTime()
- that 'builds' the house, and assigns the address of that house to the wt
variable.
threadA ta = new threadA(wt);
This makes a new instance of threadA
(and also notes the adress of it onto the local variable space), and hands a copy of your address book page to it (java is pass-by-copy, always, but wt
is the reference, not the object itself). You now have an address book page with '123 Fairfield lane' on it, and so does threadA
- threadA has its own private copy. Whatever threadA does with that address book page has no effect on your address book. But if they decide to go to the house at that address - you can see that too.
From my basic knowledge if a thread is in a while true loop you cannot interact with it
Where you say 'in a while true loop', what you really mean is 'if it is running'. It doesn't matter if it is looping, it just matters that it is running.
What this is referring to is the memory model. Modern CPUs cannot read or write memory at all. Because that memory bus is waaaay too far away, those electrons are flying through the lines on your motherboard at ~60% the speed of light or similar, and that means it's like slow molasses relative to the CPU, that's why they cannot do that. Instead, there is a small chunk of very fast memory directly on the core, split up in a few 'pages' that have a set size (Something like 64k - depends on your processor), and the only thing a CPU can do is tell the memory controller: Go write this page back to main memory (all 64k of it) at this address, then wipe it out and replace it with the contents of the main memory banks from A to B. I'll wait. All 1000 cycles of it, because, boy, I need to wait a long time for you to do this stuff because that memory bank is so far away.
Given that that is how CPUs work, java needs to 'claim' some rights because if it didn't it would run incredibly slowly. One of the rights it claims is reordering and local caching.
A JVM may cache anything you get from the heap (so any non-local) in the on-die cache of the CPU core. Which means if you have 1 field, and 2 threads are simultaneously reading and writing to that field, each thread may actually just be writing to its local copy, and the JVM is non-specific as to how it will merge this back into memory - generally, some arbitrary page write 'wins' and it's all unreliable.
In other words, if 2 threads are accessing the same bit of heap (the same field), your code is broken: Depending on the CPU, architecture, music playing on your music player as you do the operation, or the phase of the moon - you get one result or another. Ouch.
To make threading possible in the first place, the JMM (Java Memory Model) defines certain specific operations that establish 'Happens-Before'. If according to the JMM action A 'happens before' action B, then the B line, and any line that follows it, will not be able to observe state such as it was before A ran. Essentially, If A 'Happens-Before (per the JMM)' B, then A really does happen before B, unless B couldn't possibly observe this, in which case the JVM is still free to run these things in whatever order it wants.
Establishing HB can be done with synchronized
, volatile
, various peculiar actions (thread.start()
is HB relative to the first line inside that thread, for example), and of course by using (core) library functions that itself do this stuff, such as ConcurrentHashMap
and many other things from the j.u.c package.
This is, presumably, what you read and misunderstood: You should not interact (read or write) any field that another thread that has started and hasn't died yet also interacts with, unless you have carefully managed this access and ensured that all possible interactions are probably guarded by HB/HA relationships so that your code will run the same way every time and didn't turn into some crazy evil coin flip game where it depends on the flaps of the proverbial butterfly. If you break the rule and you interact with the same field from 2 separate threads without establishing HB/HA anyway - then 'it works' (nothing directly crashes, no exceptions are thrown, the code compiles fine, and will run) - but the results are more or less arbitrary:
class Example {
int x;
void crazy() {
x = 1;
new Thread(() -> x = 5).start();
new Thread(() -> x = 10).start();
System.out.println(x);
}
}
The above code can legally print 1, 5, or 10. A JVM is working properly and is fulfilling the spec no matter which one of those it happens to return - and the JVM doesn't guarantee it's random either (a JVM that always returns 1 here, is fine. A JVM that always returns 1 except it returns 10 on the 5th monday of the 3rd month - is fine). Code written like this is the problem. There's no way to know, really. It's essentially impossible to test for it (given that a JVM that always returns 1 is also fine here). The vast majority of ways you can run this (combos of JVM vendor, version, OS, architecture, etc) will print '1', but a '10' wouldn't be incorrect.
Here in this specific code, there is no problem at all - wt
the reference is written once, and this is HB relative to the thread (because it precedes t.start()
), so accessing the ref isn't an issue as it never changes, and System.currentTimeMillis()
doesn't access any fields, so the call to .getTime()
isn't problematic either.
Hence, this code works.. just fine. There is nothing wrong with it.