A major benefit that is derived out of the introduction of a JVM is the resulting platform independence for a Java executable. As seen in the previous section, the JVM provides a run time environment for Java executable. Also, we have seen that the JVM is a software program that runs on a target environment. Thus, as long as we have a JVM for a desired machine, our compiled Java code (bytecode) will run on that machine without requiring any changes in the binary or the source program. This way, a Java program becomes platform independent at the binary level. Any compiled Java application would run on any platform without any changes as long as the target platform provides a Java virtual machine. This turned out to be a great boon for application developers who could now easily create a portable application that would run on several operating systems without any code modifications. In the days before the introduction of Java, creating portable applications was a painstaking task for many developers. These developers lost important revenue if they were unable to port their popular applications onto other platforms—and those who were able still had to rewrite them for new devices.
A Java compiler compiles the given source program into machine independent bytecode. If the machines run a different operating system—that is, UNIX, Windows, and Mac OS, Each of these machines could be running on totally different hardware. For example, UNIX could be running on a Sparc workstation, Windows could be running on an Intel 80xx architecture, and Mac OS could be running on an architecture such as 68xxx or Intel 80xx. However, what is common among all three machines is the JVM.
Note that the JVM, which is a software application itself, is not portable. Rather, every JVM is written for a particular target platform. The bytecode is portable across different JVMs and thus across different platforms that support the JVMs.
The fact that Java was a multi threaded language was one of the key features hyped when the language was introduced in 1996 (…as if other languages did not support multithreading). Although most other languages do support threading, what Java provided was simpler semantics for threading. Creating threads in C++ is as easy as creating threads in Java. The difference lies in the management of shared resources. To share a resource among multiple running threads, C++ uses several constructs, including critical sections and semaphores. These are low-level constructs provided by the operating system itself. Coding for these constructs is typically very complex. Java hides the use of all these constructs underneath a newly designed keyword called synchronized. We just declare a block of code or a class as synchronized and the Java runtime takes care of the thread concurrency in accessing the shared resources. This makes thread programming in Java very simple.