|
|
![]() |
|
|
iSeries Java Threading Considerations by Kevin Vandever In "Calling RPG Native Methods From Java," I introduced you to a very cool technique that allows you to call RPG subprocedures from Java. I also warned you that this is not yet the best way to get at your subprocedures from Java, primarily because of how the iSeries handles threading. Well, in this article, I am going to depart from the usual how-to style and, instead, discuss some theory. I know, I know. Yuk, who needs theory? But it is important that you understand iSeries threading if you are going to mix RPG and Java on your iSeries.
I Don't Sew, Why Should I Care About Threading?A thread is an independent, lightweight control activity within a computer process--or a "job," in iSeries speak. It is not the job; rather, it is an activity within a job. It is how Java and the Java Virtual Machine work, even on the iSeries. The same method, or many methods, can be called multiple times, simultaneously, within the same process. This is an efficient way to do work, as it reduces the need to start multiple processes and therefore uses less system resources. On the iSeries, we're accustomed to our programs running in individual jobs. If I want to run an RPG program multiple times, simultaneously, I do so inside multiple jobs. In a multithreaded application, many threads typically exist within a single process and share the same address space. So if Java runs in a multithreaded environment and iSeries programs do not, why I am writing this article? Forget it! Go on and read one of the other fascinating articles in this issue. There's nothing more to read here--or is there? That's right, it isn't that simple. You may not be able to spawn (fancy word for run) multiple threads from within RPG, but an RPG subprocedure can be spawned from a Java thread in a multithreaded environment, and if you are to allow such a thing to happen you had better practice safe threading. Thread safety for an application is the ability of that application to run in a multithreaded environment. Simple, huh? An application that is not thread-safe is not guaranteed to run correctly in a multithreaded environment, even if the application itself does not directly employ multithreading, which is the case when a native method (iSeries subprocedure) is called locally from Java using Java Native Interface. The subprocedure does not employ the multithreading, but still it is not safe in a multithreaded environment. That's because iSeries objects use global variables and data structures that are shared by all threads in a process. In a multithreaded environment, unrestricted access to these global data structures and variables can result in unpredictable behavior. To avoid this mess, you need to ensure that access to the global resources is serialized, so that no two threads can access the resources simultaneously. This is accomplished by protecting the resources under a lock. Remembrance of Things PastSo how do you obtain such a lock? You do so by inserting an H-spec in your modules and specifying the Thread(*SERIALIZE) keyword/parameter combination. Check out my source code in "Calling RPG Native Methods From Java" for an example. Basically, this means that if two Java threads call the same RPG module within the same job, Thread(*SERIALIZE) states that the first call to a module must completely finish before the second one is called. Even though the Java runs in a multithreaded environment and RPG is not yet a multithread-capable language, you have protected your modules' global resources by serializing the calls. Pretty cool, huh? Well, all isn't rosy yet. There is a potential performance hit with the synchronization of module calls on the ILE side. Remember, RPG is thread-safe, but it is not thread-enabled. OS/400 will handle the synchronization of multiple threads, provided you code Thread(*SERIALIZE) in your service program. This means that simultaneous calls to the same module, within a single job, cause an increase in synchronization, which will subsequently slow down each thread's completion time. In fact, IBM recommends using Java Native Interface only when there is no alternative. But if you must use it, it is best used when the service program has to do a considerable amount of work and will not be called all that often. More Potential TroubleNow that you might be a bit down on this whole threading thing, it's time to kick your dog, too (don't worry, no animals were harmed in the writing of this article). There are times when you may think you are practicing safe threading, yet you might as well be playing Russian roulette with six bullets. Employing Thread(*SERIALIZE) will ensure sequential access to global data used within each module; that is to say that it will ensure that only one thread is active in any one module at a time. However, it will not ensure sequential access to shared data across modules. Two or more modules can share data when data files are shared across modules or when data is based on a pointer and that pointer is accessible to multiple modules. For example, subprocedure A in module A can pass a pointer to subprocedure B in module B, and subprocedure B can save that pointer in static storage. Now both modules have access to that data. Let's say a thread in module A is accessing that data. Once subprocedure B ends, a new thread could call subprocedure B, or any other subprocedure in module B, and access that same data. Oops! Serialization does not stop simultaneous access to the same storage in each module. There is no magic potion, silver bullet, or pill to ensure 100 percent thread safety. You have to structure your applications so that this situation doesn't happen. That's right; abstain from simultaneous data access between modules. If you cannot restructure and you have to access resources simultaneously from multiple threads, you can still synchronize that access using advanced threading technologies such semaphores and mutexes, so you have that going for you, which is nice. Hide the Fine ChinaNo more animals to kick? Well that's a good thing, because there is one more situation that serialization can't help combat and that is module deadlock. Let's say you have two modules, module A and module B. Both have been correctly coded with Thread(*SERIALIZE). Module A contains subprocedures 1 and 2, while module B contains subprocedures 3 and 4. Now, let's say that subprocedures 1 and 3 are being called at the same time by two separate threads. So far, so good, right? Now let's say subprocedure 1 in module A attempts to call subprocedure 4 in module B. Serialization will cause that call to wait until module B is freed up by its active thread. Now, what if subprocedure 3 in module B attempts to call subprocedure 2 in module A? Right, serialization will cause that call to wait until module A is freed up by its active thread. So module A is waiting for module B, which is in turn waiting for module A, which is in turn waiting--well, you get the picture. We have deadlock. You cannot access more than one subprocedure in a module using ILE RPG synchronization techniques. You have to either restructure your application or employ C synchronization techniques that are better equipped to deal with this situation. You Can Come Out NowWow! That's some heavy stuff, man. Next time I'll go back to "how to" articles. You should probably not read this on an empty stomach or before operating heavy machinery or driving of any kind. You should not read this if you are pregnant, want to get pregnant, or know someone who is pregnant. Do not fly for at least 24 hours after reading this, but whatever you do, don't stop using Java on the iSeries. If you understand what's going on, you'll be OK. Besides, each new release of OS/400 promises to bring improvements in Java interoperability. Reference
|
Editors
Contact the Editors |
|
Last Updated: 6/20/02 Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved. |