jpibay
jpibay

Reputation: 11

A Java beginner question regarding multi-threading

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

Answers (3)

jpibay
jpibay

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

Highbrainer
Highbrainer

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.

Complete code with thread safety problem

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...

Complete code with thread safety fixed

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

nuwan
nuwan

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

Related Questions