DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. Introduction to the JR Programming Language

Introduction to the JR Programming Language

Baptiste Wicht user avatar by
Baptiste Wicht
·
Jun. 29, 10 · Interview
Like (0)
Save
Tweet
Share
8.61K Views

Join the DZone community and get the full member experience.

Join For Free

JR is a programming language especially created to solve concurrent programming problems. This language extends Java, adding the main paradigms of concurrent programming. Moreover JR makes the concepts implemented in Java like process or semaphores easier. There are also several extensions for JR to add more functionality like monitors and Conditional Critical Region (CCR). JR is the implementation of the SR language for Java. JR just adds a layer over Java. Once we use the JR compiler, the JR source files are transformed into Java files and are executed by the virtual machine like any other Java class.

JR is often used as a school support to learn concurrent programming. In this article, we will see the basis of programming with JR. The presented version is from June 2009,  version 2.00602 which is based on Java 6.0.

This article needs you to have installed the JR environment on your system. An article is available here for the installation under Windows.

In this article, we will especially focus on the parts of the JR language for  concurrent programming. We will not see the the integrality of the language. JR has other benefits than to make concurrent programming easier, but we will not see that in this article. Moreover, all the aspects of concurrent programming in JR will not be treated here.

Hello World

Like all other languages, we must start with a simple Hello World. Thus we’ll create a file Hello.jr. Nothing special here, it’s pure Java :

public class Hello {
public static void main(String[] args){
System.out.println("Hello World");
}
}

Then we can compile it :

jrc Hello.jr

This will create a jrGen folder containing Java files. The result of a JR compilation is always a set of Java files corresponding to the translation of the JR files.

To launch your JR program, use the jr command followed by the name of the main class (class containing the main method) :

jr Hello

That will display :

Hello World

The jr command will also launch the compilation of Java files. This compilation will be done every time. If you want to make only the launch of compiled files, you can use the jrrun command.

Like said in introduction, the JR language extends the Java language. So, you can code in Java with JR. Thus an Hello World is only Java.

Processes

The first thing to see is the declaration of processes. This is done in an easier way than in Java. No need to instanciate some objects, this is done in a declarative way and JR make the rest.

For the declaration of process, JR introduce a new keyword process who enable to declare a process. Here is the simplest declaration of a process :

process Hello {
System.out.println("Processus");
}

Like you can see it, it’s easier than in Java. And better, no need to launch it, you just have to instantiate the class. A process can also be declared static. This time it will not be launched at the instantiation of the class but at the resolution of the class by the virtual machine. By example, we can rewrite an HelloWorld in that way :

public class HelloProcess {
static process Hello {
System.out.println("Hello World");
}

public static void main(String[] args){}
}

which displays exactly the same thing as the first version of  Hello World. The difference is that our display is made from a thread.

Moreover, JR enables you to declare a big set of threads in a declaration with the following syntax :

static process Hello((int id = 0; id < n; id++)){}

This will declare n threads. The syntax is the same as the for loop. Let’s declare 25 threads Hello World :

public class HelloProcess {
static process Hello((int id = 0; id < 25; id++)){
System.out.println("Hello World from thread " + id);
}

public static void main(String[] args){}
}

When we launch that, we could have the following result :

Hello World from thread 2
Hello World from thread 24
Hello World from thread 11
Hello World from thread 22
Hello World from thread 0
Hello World from thread 4
Hello World from thread 6
Hello World from thread 8
Hello World from thread 10
Hello World from thread 12
Hello World from thread 14
Hello World from thread 16
Hello World from thread 18
Hello World from thread 20
Hello World from thread 23
Hello World from thread 21
Hello World from thread 19
Hello World from thread 17
Hello World from thread 15
Hello World from thread 13
Hello World from thread 9
Hello World from thread 7
Hello World from thread 5
Hello World from thread 3
Hello World from thread 1

Like you can see, if you launch the program several times the display is not the same and the message comes in a completely different order at each launch. Nothing can guarantee the order the threads launches and still less the order of the execution of the instructions and you must not count on it.
It’s the basis of the concurrent programming. You cannot predict the order of the instructions in the different threads.

Quiescence action

JR introduces a new, powerful concept: the quiescence action. It’s an action that's executed when the system is quiescent. It seems that all the process are finished or remain in deadlocks.

Before that, we used to introduce the concept of operations. In our case, an operation is a simple method declared with the op keyword. But in JR an operation is more than a method and could be invoked in different ways and permit other things that methods, but this is beyond the scope of this article.

Here is a declaration of a simple operation.

public static op void end(){
System.out.println("End");
}

So, this is a simple method with the op prefix. You can invoke it like any other method. But you can also declare it like the action to execute when the system is in quiescence state. If we take our example of the 25 hello world and if we define the quiescence action here is what we get :

import edu.ucdavis.jr.JR;

public class QuiescenceProcess {
static process Hello((int id = 0; id < 25; id++)){
System.out.println("Hello World from thread " + id);
}

public static void main(String[] args){
try {
JR.registerQuiescenceAction(end);
} catch (edu.ucdavis.jr.QuiescenceRegistrationException e){
e.printStackTrace();
}
}

public static op void end(){
System.out.println("End");
}
}

We use the registerQuiescenceAction(op) method of the JR class. This class provide some utility methods for the JR programs
And a launch, we see something like that :

Hello World from thread 0
Hello World from thread 22
Hello World from thread 23
Hello World from thread 21
Hello World from thread 24
Hello World from thread 20
Hello World from thread 19
Hello World from thread 18
Hello World from thread 17
Hello World from thread 16
Hello World from thread 15
Hello World from thread 14
Hello World from thread 13
Hello World from thread 12
Hello World from thread 11
Hello World from thread 10
Hello World from thread 9
Hello World from thread 8
Hello World from thread 7
Hello World from thread 6
Hello World from thread 5
Hello World from thread 4
Hello World from thread 3
Hello World from thread 2
Hello World from thread 1
End

This is really useful to execute an action after the end of the system and verify something on the system. By example, display a message in the case of a deadlock or display debug informations on the executed operations.

 

 

Semaphores

We will now see how to use one of the basic concept of concurrent programming : the semaphores. The semaphores are a really simple concept but very powerful. A semaphore represent a certain integer valur representing the number of threads that can go in a certain portion of code, call it “s”. A semaphore has two actions :

  • P : make the thread wait while s equals 0 and then decrement s.
  • V : increment s.

This two operations are atomic. We use the semaphores to protect a critical section who need to be executed in an atomic way. We can also use semaphores to restrain the number of threads that can execute some instructions in parralel.

The declaration and the use of semaphores is really easy. Here is how you can declare a semaphore with a initial value of 1 :

sem mutex  = 1;

Then, the operations P et V are extremely simple to use :

P(mutex);
//Critical section
V(mutex);

The semaphores are principally used to solve the critical section problem. Imagine a simple example, but who show exactly the problem that the semaphore can solve :

private static int value = 0;

static process Calculator((int id = 0; id < 50; id++)){
for(int i = 0; i < 5; i++){
value = value + 2;
}
}

Because this code launch 50 threads who add each one 5 times 2 to value, we could think that value must be 500 at the end of the execution, isnt’it ?

But nothing guarantee that result. This is known as the interleaving in concurrent programming. This is because the + 2 operation is in reality 3 operations :

  • read the value of value
  • add 2 to the read value
  • set the new value to value

A thread can be put in wait just after 1 and do the incrementation on the old value but other threads have still made the incrementation, but it has the old value when it mades the +2 and write and false value to value. To prove that to you, execute the following code several times :

import edu.ucdavis.jr.JR;

public class SemaphoreProcess {
private static int value = 0;

static process Calculator((int id = 0; id < 50; id++)){
for(int i = 0; i < 5; i++){
value = value + 2;
}
}

public static void main(String[] args){
try {
JR.registerQuiescenceAction(end);
} catch (edu.ucdavis.jr.QuiescenceRegistrationException e){
e.printStackTrace();
}
}

public static op void end(){
System.out.println(value);
}
}

On my computer, i’ve the following results.

498
500
500
500
496

And if we use greater values, it’s even worse. By example, with 100 threads and 100 iterations :

20000
19560
19912
19758
20000

But this problem can be solved with semaphores :

private static sem mutex = 1;
private static int value = 0;

static process Calculator((int id = 0; id < 50; id++)){
for(int i = 0; i < 5; i++){
P(mutex);
value = value + 2;
V(mutex);
}
}

With that, we have the guarantee that only one thread can do the incrementation at a time and make it atomic. Then all the executions will finish with a value of 500. But that of course impact the performances because instead of x threads who made operations in a parralel way, we’ve now only one thread at a time. The example with 100 threads and 100 iterations is really slow. We can improve performance using the mutex semaphore around the loop. But the performances are to be considered differently in each example. So we must use the synchronization methods of threads wisely.

Conclusion

So, we’ve now discover the main concepts of the JR programming language. Like you have see in this article, this programming language makes concurrent programming concepts easier.

I hope that this article has been useful to you to discover the JR Programming Language, and why not learn and use this language.

From http://www.baptiste-wicht.com/2010/01/jr-introduction/

Java (programming language) Semaphore (programming) Concept (generic programming) Critical section IT Virtual Machine Execution (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 11 Observability Tools You Should Know
  • Steel Threads Are a Technique That Will Make You a Better Engineer
  • Building a Real-Time App With Spring Boot, Cassandra, Pulsar, React, and Hilla
  • Spring Boot, Quarkus, or Micronaut?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: