Reputation: 11
I'm brushing up my java skills after years of not using it. Recently, I've been reading a chapter on multi-threading that mentions:
"However—what if you have a non-static method that accesses a static field? Or a static method that accesses a non-static field (using an instance)? In these cases things start to get messy quickly, and there's a very good chance that things will not work the way you want. If you've got a static method accessing a non-static field, and you synchronize the method, you acquire a lock on the Class object. But what if there's another method that also accesses the non-static field, this time using a non-static method? It probably synchronizes on the current instance (this) instead. Remember that a static synchronized method and a non-static synchronized method will not block each other—they can run at the same time."
For the sake of learning, I've been trying to code an example where a scenario that was cited on the snippet above: A program in which a static synchronized method that modifies a non-static field running at the same time with a non-synchronized method.
So, far I was not successful in doing this. A bit of help is appreciated. Thanks!
Below is the sample of a code I have done. However, as expected, the threads do not run at the same time since they are synchronized. I just wanted to see an example mentioned on the book so I will know what not to do.
class code147_class1{
int num1 = 111;
static int num2 = 222;
}//code147_class1
public class code147 implements Runnable {
code147_class1 z = new code147_class1();
public static void main (String args[]){
System.out.println("Program has started...");
System.out.println("The value of the instance variable is:"+new code147_class1().num1);
System.out.println("The value of the static variable is:"+code147_class1.num2);
code147 cd1 = new code147();
Thread thread1 = new Thread(cd1);
Thread thread2 = new Thread(cd1);
thread1.start();
thread2.start();
}//main
public void run(){
System.out.println("Thread has started for:"+Thread.currentThread().getId());
try{
subtract_instance_var(z);
Thread.sleep(100);
code147.static_subtract_instance_var(z);
}
catch(Exception ex){
}
}//run
public synchronized void subtract_instance_var(code147_class1 x){
if(x.num1 ==111){
try{
Thread.sleep(100);
}
catch(Exception ex){
}
x.num1 = x.num1 - 11;
System.out.println("Value is subtracted at thread:"+Thread.currentThread().getId());
}//if
System.out.println("The value of instance variable at the end for thread: "+Thread.currentThread().getId()+" is "+x.num1);
}//subtract_instance_var
public synchronized static void static_subtract_instance_var(code147_class1 x){
if (x.num1==111){
try{
Thread.sleep(100);
}
catch(InterruptedException ex){
}//catch
x.num1 = x.num1 -11;
System.out.println("Value is subtracted at thread:"+Thread.currentThread().getId());
}//if
System.out.println("STATIC The value of instance variable at the end for thread: "+Thread.currentThread().getId()+" is "+x.num1);
}//stati_subtract_var
}//class
After running the code, I was expecting the value of the instance variable to be 89. The program however results in 100.
Upvotes: 0
Views: 122
Reputation: 11
With inputs from @Highbrainer, I managed to get a program to run 2 threads with 1 running a synchronized instance method and another running a static method. Both modifying an instance field. As advised from the book, to prevent this kind of problem, we should always be modifying instance fields from an instance method and static fields from a s static method. I just modified the program to change the static method to an instance method.
class code148_class1{
int num1 = 111;
}//code148_class1
public class code148 implements Runnable {
static code148_class1 z = new code148_class1();
public static void main (String args[]){
System.out.println("Program has started...");
System.out.println("The value of the instance variable is:"+z.num1);
code148 cd1 = new code148();
Thread thread1 = new Thread(
()->{
cd1.subtract_instance_var(z);
}
);
Thread thread2 = new Thread(
()->{
cd1.NONstatic_subtract_instance_var(z);
}
);
thread1.start();
thread2.start();
}//main
public void run(){
System.out.println("Thread has started for:"+Thread.currentThread().getId());
// try{
// subtract_instance_var(z);
// Thread.sleep(100);
// code148.static_subtract_instance_var(z);
// }
// catch(Exception ex){
//
// }
}//run
public synchronized void subtract_instance_var(code148_class1 x){
if(x.num1 ==111){
try{
Thread.sleep(100);
}
catch(Exception ex){
}
x.num1 = x.num1 - 11;
System.out.println("Value is subtracted at thread:"+Thread.currentThread().getId());
}//if
System.out.println("The value of instance variable at the end for thread: "+Thread.currentThread().getId()+" is "+x.num1);
}//subtract_instance_var
public synchronized void NONstatic_subtract_instance_var(code148_class1 x){
if (x.num1==111){
try{
Thread.sleep(100);
}
catch(InterruptedException ex){
}//catch
x.num1 = x.num1 -11;
System.out.println("Value is subtracted at thread:"+Thread.currentThread().getId());
}//if
System.out.println("STATIC The value of instance variable at the end for thread: "+Thread.currentThread().getId()+" is "+x.num1);
}//stati_subtract_var
}//class
Upvotes: 0
Reputation: 760
Your example code does not execute the static substraction and the instance substraction at the same time. So no Thread-safety is engaged.
Also,your code only substracts 11 if the original value is 111. So your result is 100.
Here is an alternate main that will execute substract and static_substract in concurrency.
public static void main(String args[]) {
System.out.println("Program has started...");
System.out.println("The value of the instance variable is:" + new Code147_class1().num1);
System.out.println("The value of the static variable is:" + Code147_class1.num2);
Code147 cd1 = new Code147();
Thread thread1 = new Thread(() -> {
IntStream.range(0, 5).forEach(i->{
cd1.subtract_instance_var(cd1.z);
});
}, "instance thread");
Thread thread2 = new Thread(() -> {
IntStream.range(0, 5).forEach(i->{
static_subtract_instance_var(cd1.z);
});
}, "static thread");
thread1.start();
thread2.start();
}// main
Notice that in this code two threads substract 11, 5 times each, from the initial 111. The left over should be 1. It won't always be, because of thread safety.
import java.util.stream.IntStream;
class Code147_class1 {
int num1 = 111;
}// Code147_OK_class1
public class Code147 {
Code147_OK_class1 z = new Code147_OK_class1();
public static void main(String args[]) {
System.out.println("Program has started...");
System.out.println("The value of the instance variable is:" + new Code147_OK_class1().num1);
Code147 cd1 = new Code147();
Thread thread1 = new Thread(() -> {
IntStream.range(0, 5).forEach(i -> {
System.out.println("\tInstance Substract 11 #" + (i + 1));
cd1.subtract_instance_var(cd1.z);
});
}, "instance thread");
Thread thread2 = new Thread(() -> {
IntStream.range(0, 5).forEach(i -> {
System.out.println("\tStatic Substract 11 #" + (i + 1));
static_subtract_instance_var(cd1.z);
});
}, "static thread");
thread1.start();
thread2.start();
}// main
public synchronized void subtract_instance_var(Code147_OK_class1 x) {
// if (x.num1 == 111) {
try {
Thread.sleep(100);
} catch (Exception ex) {
}
x.num1 = x.num1 - 11;
System.out.println("Value is subtracted at thread T" + Thread.currentThread().getId());
// } // if
System.out.println("The value of instance variable at the end for thread T" + Thread.currentThread().getId()
+ " is " + x.num1);
}// subtract_instance_var
public synchronized static void static_subtract_instance_var(Code147_OK_class1 x) {
// if (x.num1 == 111) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
} // catch
x.num1 = x.num1 - 11;
System.out.println("STATIC Value is subtracted at thread T" + Thread.currentThread().getId());
// } // if
System.out.println("STATIC The value of instance variable at the end for thread T"
+ Thread.currentThread().getId() + " is " + x.num1);
}// stati_subtract_var
}// class
Example Output
Program has started... The value of the instance variable is:111 Instance Substract 11 #1 Static Substract 11 #1 STATIC Value is subtracted at thread T11 STATIC The value of instance variable at the end for thread T11 is 89 Static Substract 11 #2 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 89 Instance Substract 11 #2 Value is subtracted at thread T10 STATIC Value is subtracted at thread T11 The value of instance variable at the end for thread T10 is 67 Instance Substract 11 #3 STATIC The value of instance variable at the end for thread T11 is 67 Static Substract 11 #3 STATIC Value is subtracted at thread T11 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 45 Instance Substract 11 #4 STATIC The value of instance variable at the end for thread T11 is 45 Static Substract 11 #4 Value is subtracted at thread T10 STATIC Value is subtracted at thread T11 The value of instance variable at the end for thread T10 is 23 Instance Substract 11 #5 STATIC The value of instance variable at the end for thread T11 is 23 Static Substract 11 #5 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 12 STATIC Value is subtracted at thread T11 STATIC The value of instance variable at the end for thread T11 is 12
Notice the last value is 12, not 1...
You could make your program thread safe by synchronizing on the same monitor, say z in this example :
import java.util.stream.IntStream;
class Code147_OK_class1 {
int num1 = 111;
}// Code147_OK_class1
public class Code148_OK {
Code147_class1 z = new Code147_class1();
public static void main(String args[]) {
System.out.println("Program has started...");
System.out.println("The value of the instance variable is:" + new Code147_class1().num1);
Code148_OK cd1 = new Code148_OK();
Thread thread1 = new Thread(() -> {
IntStream.range(0, 5).forEach(i -> {
System.out.println("\tInstance Substract 11 #" + (i + 1));
cd1.subtract_instance_var(cd1.z);
});
}, "instance thread");
Thread thread2 = new Thread(() -> {
IntStream.range(0, 5).forEach(i -> {
System.out.println("\tStatic Substract 11 #" + (i + 1));
static_subtract_instance_var(cd1.z);
});
}, "static thread");
thread1.start();
thread2.start();
}// main
public /* synchronized */ void subtract_instance_var(Code147_class1 x) {
synchronized (x) {
// if (x.num1 == 111) {
try {
Thread.sleep(100);
} catch (Exception ex) {
}
x.num1 = x.num1 - 11;
System.out.println("Value is subtracted at thread T" + Thread.currentThread().getId());
// } // if
System.out.println("The value of instance variable at the end for thread T" + Thread.currentThread().getId()
+ " is " + x.num1);
}
}// subtract_instance_var
public /* synchronized */ static void static_subtract_instance_var(Code147_class1 x) {
synchronized (x) {
// if (x.num1 == 111) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
} // catch
x.num1 = x.num1 - 11;
System.out.println("STATIC Value is subtracted at thread T" + Thread.currentThread().getId());
// } // if
System.out.println("STATIC The value of instance variable at the end for thread T"
+ Thread.currentThread().getId() + " is " + x.num1);
}
}// stati_subtract_var
}// class
Output
Program has started... The value of the instance variable is:111 Instance Substract 11 #1 Static Substract 11 #1 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 100 Instance Substract 11 #2 STATIC Value is subtracted at thread T11 STATIC The value of instance variable at the end for thread T11 is 89 Static Substract 11 #2 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 78 Instance Substract 11 #3 STATIC Value is subtracted at thread T11 STATIC The value of instance variable at the end for thread T11 is 67 Static Substract 11 #3 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 56 Instance Substract 11 #4 STATIC Value is subtracted at thread T11 STATIC The value of instance variable at the end for thread T11 is 45 Static Substract 11 #4 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 34 Instance Substract 11 #5 STATIC Value is subtracted at thread T11 STATIC The value of instance variable at the end for thread T11 is 23 Static Substract 11 #5 Value is subtracted at thread T10 The value of instance variable at the end for thread T10 is 12 STATIC Value is subtracted at thread T11 STATIC The value of instance variable at the end for thread T11 is 1
Tell me if the java 8 lambda stuff is unclear, I will rewrite it with "classic" Runnables and loops.
HTH!
Upvotes: 1
Reputation: 90
The result 100 you got is correct. thread1 and thread2 will run at the same time. Since "subtract_instance_var" method is synchronized one thread will make the variable 100. Then that thread will go to sleep. As the lock is released other thread can execute "subtract_instance_var". But nothing will happen as "x.num1==111" condition fails. After sleep when both the threads try to execute "static_subtract_instance_var" method still "x.num1==111" condition fails. So variable value remains 100.
Upvotes: 1