Threads can be single or multiple with each thread having it’s own thread stack.
The thread stack contains all local variables for each method being executed (all methods on the call stack). A thread can only access it’s own thread stack. The heap contains all objects created in your Java application, regardless of what thread created the object.
Single Threaded:
In a single threaded system, we only have a single thread which is running on a CPU. Since it is a single thread, it doesn’t share data. Also it utlilizes the CPU better. But modern CPU’s which have multiple processors, single threaded systems can’t make efficient use of thta since that thread would make efficient use of only one processor. That is the only drawback.
Same Threaded:
Same threaded system is a type of Multi Threaded System in which threads don’t share data. So here each thread is running on a separate CPU in parallel. So it utilizes the modern CPU’s better which are multiple core. In old CPU’s which means single CPU’s, they run alternately with CPU switching between the threads.
Multi Threaded:
In multi threaded system, multiple threads are running in parallel and they share the same data. So various aspects such as data synchronization and consistency need to be guraranteed to avoid race conditions.
Java concurrency models guarantees data consistency where multiple threads are used.
Now let us look at the different types of models:
Parallel Workers:
Here, a delegator delegates the jobs to different workers. For example, there are 3 flats which require maintainance and furnishing. So the delegator delegates those 3 jobs to 3 workers and they work in parallel improving the efficiency.
Similarly at the system level, delegator delegates jobs to multiple workers(you can say threads) and those workers execute in parallel in different cpu’s. So the performance here is efficient as the model makes efficient use of cpu’s.
But the problem arises when it is shared architecture. That is the workers which can be threads can share some data. So here comes problems such as race condition.So with proper use of synchronization, such problems can be avoided if it is a shared architecture.
Assembly Line:
This is one of the most efficient non blocking concurrency model.
It is similar to workers workign in factory. They do a task and forward the same to the next worker for further processing.
So for example in assembly line, the first worker reads a file then forwards the result to the next worker who does some processing on the file data and forwards it to next worker for further processing.
When the second worker was doing some processing, the first worker started reading next set of files. So here it is totally a non blocking I/O operation. Non-blocking IO means that when a worker starts an IO operation (e.g. reading a file or data from a network connection) the worker does not wait for the IO call to finish. Whatever data it has read it can forward to the next worker on the assembly line and then read the remaining file. By doing this, both the workers are non blocking.
Another benefit of assembly line model is that it is a non shared architecture. That is no data whether an object or a data structure is common or you can say shared between multiple workers.So concurrency problems like race conditions are avoided and there is no need to implement synchronization.
The only problem is that the code base is huge for this model since each worker has it’s own business logic. So code analysing may take time.
So to summarize, assembly line is kind of a reactive driven system.
Functional Parallelism:
The basic idea of functional parallelism is that you implement your program using function calls. Functions can be seen as actors that send messages to each other, just like in the assembly line concurrency model. When one function calls another, that is similar to sending a message.
With Java 7 we got the java.util.concurrent package contains the ForkAndJoinPool which can help you implement something similar to functional parallelism. With Java 8 we got parallel streams which can help you parallelize the iteration of large collections
A final variable in Java can be used in different contexts which are:
final variable
When the final keyword is used with a variable then the value of the variable can’t be changed once assigned. In case no value has been assigned to the final variable then a value can be assigned to it only through the class constructor
final method
When a method is declared final then it can’t be overridden by the inheriting class. It is done to implement a standard fuctionality which all sub classes should inherit.
final class
When a class is declared as final in Java, it can’t be extended by any subclass class but it can extend other class
In java, there is an concept of synchronized block in which only one thread can access the block at a time. So it means the block is locked for other threads and they cannot access it.
Block is synchronized to avoid simultaneous access by multiple threads which may result in inconsistent or you can say dirty reads. It is done to avoid race conditions.
A synchronized block in Java is synchronized on some object. All synchronized blocks synchronized on the same object can only have one thread executing inside them at the same time. All other threads trying to enter the synchronized block would be blocked until the thread inside the synchronized block exits the block.
To mark a block as synchronized,we use the “synchronized” keyword.
Ok so in which places we can apply synchronization keyword or you can say apply synchronization?
1)With instance methods – Here lock would be at the object level and entire method would be synchronized. Since lock would be at the object level, only one thread can access it at a time if there are multiple threads created on the same object.
2)With Static Methods – Here lock would be at the class level and entire method would be synchronized. Since lock would be at the class level, only one thread can access it in the whole JVM. A way more restrictive lock compared to above.
3)Inside Instance methods – Here a block would be synchronized inside the method. Since it is an instance method, lock would be on the Object.
4)Inside Static Instance methods – Here a block would be synchronized inside the method. Since it is an static method, lock would be on the Class.
Let’s look at some code examples depicting the synchronized block.
//Synchronized on Instance Method
public class TestInstanceSync{
private int cnt = 0;
public synchronized void add(int val){
this.cnt += val;
}
}
//Synchronized on Static Method
public static TestStaticSync{
private static int cnt = 0;
public static synchronized void add(int val)
{
cnt += val;
}
}
In the first example, only one thread can execute the method at a time per instance. Other threads on the same instance would be blocked till the end of the execution of the method by that one thread.
In the 2nd example, only one thread or you can say process can execute that sync method since it is static sync. Static means at te class level. So lock would be at te class level.
In case a class contains more than one static synchronized method, only one thread can execute inside any of these methods at the same time.
Now let’s take a look at the synchronized block:
public void add(int val){
synchronized(this){
this.cnt += val;
}
}
If you see the block inside the instance method. So the entire method is not locked and the lock is placed only on that small block.
Similarly for static method, we can have such a similar block.
JVM which stands for Java Virtual Machine can be considered as the heart of java.
It converts Java bytecode into machines code or you can say machine language. JVM is a part of Java Run Environment (JRE).
The bytecode which is converted by the JVM is created by the java compiler. The compiler converts the .java code or you can say the source code to byte code.
JVM is not platform-independent, that’s why you have different JVM for different operating systems.
The primary advantage of Java JVM is code compatibility as it eases a programmer’s job to write code only once and run anywhere. Once the application is built it can be run on any device that has JVM. So the job of the programmer is to write only the java code. The compiler and then the JVM takes care of the best.
Src:wiki commons
As seen above, the execution engine executes the bytecode which is assigned to the runtime data areas through the classloader.
Inheritance: Inheritance is a process where one class acquires the properties and methods of another. Eg:, Class A extends B
Encapsulation: Encapsulation in Java is a mechanism of wrapping up the data and code together as a single unit. In encapsulation, the variables of a class will be hidden from other classes, and can be accessed only through the methods of their current class. Eg.; private variable.
Abstraction: Abstraction is the technique of hiding the implementation details from the user and only providing the functionality to the users. In java it is achieved through Interfaces and Abstract classes.
Polymorphism: Polymorphism is the ability of a variable, function or object to take multiple forms or you can sau exist in multiple forms. There are two types of polymorphism in Java: compile-time polymorphism and runtime polymorphism. We can perform polymorphism in java by method overloading and method overriding.
In java, as you know a class is made up of member variables and methods. But is there a way to restrict access to those?
Yes!! Java provides 4 kinds of access modifiers.
Visibility
Default
Public
Private
Protected
In same class
YES
YES
YES
YES
Same Package and subclass
YES
YES
NO
YES
Same Package but non subclass
YES
YES
NO
YES
Different package and subclass
NO
YES
NO
YES
Different package but non subclass
NO
YES
NO
NO
So as per the table defined above, public is the least restrictive access. The public methods and member variables are accessible anywhere.
Also per the tabular format above, Java has private as its most restrictive access. The private methods and member variables are accessible only in the same class.
So if any other class wants to access private variables, how it can be done?
We can do the same with the help of public getter methods.
Class Employee
{
private String name = "John";
private String email = "john@abc.com";
//Getters
public String getName()
{
return name;
}
public String getEmail()
{
return email;
}
//Setters
public void setName(String name)
{
this.name = name;
}
public void setEmail(String email)
{
this.email = email;
}
}
As seen above using getters, we can fetch/access private variable values. So even if the name of the variable name is changed to empName in the class, since it is private it would not affect any other classes who access the variable since those classes would access it only through the getter.
You can access the private methods of a class using java reflection package.
To compare 2 objects in Java, java has provided two interfaces.
1)Comparable
2)Comparator
Let’s take a look into it one by one.
Comparable:
A comparable object compares itself with another object. The class for which the objects need to be compared must implements the java.lang.Comparable interface to compare its instances.
Let’s take an example.
Consider a class Employee which has 2 member variables which are employee name and salary. There are 2 instances of it which are emp1 and emp2. In emp1 object, name is John and Salary is 10k.In emp2, name is david and salary is 20k. Let’s sort it using salary.
We can implement the Comparable interface with the Employee class, and we override the method compareTo() of Comparable interface. Kindly check below.
Class Employee implements Comparable
{
private String name;
private Integer salary;
public Employee (String name, Integer salary)
{
this.name = name;
this.salary = salary;
}
public int compareTo(Employee emp)
{
this.salary - emp.salary;
}
public String getName()
{
return name;
}
public String getSalary()
{
return salary;
}
}
//Now let's call this class.
Class ComparableTest
{
public static void main(String[] args)
{
ArrayList<Employee> list = new ArrayList<Employee>();
list.add(new Employee("John", 5000));
list.add(new Employee("David", 10000));
list.add(new Employee("Rob", 20000));
Collections.sort(list);\\Line 8
System.out.println("Employees after sorting : ");
for (Employee emp: list)
{
System.out.println(emp.getName() + " " +
emp.getSalary());
}
}
}
If you take a look into the compareTo method in Employee class, it is comparing basis salary. In main class ComparableTest at Line 8, we call Collections.sort method to finally sort the list.
Comparator:
Unlike Comparable, Comparator is a separate class. We create multiple separate classes (that implement Comparator) to compare by different members. Collections class has a second sort() method which takes Comparator class object as 2nd parameter. The sort() method invokes the compare() to sort objects.
class EmpCompare implements Comparator<Employee>
{
public int compare(Employee e1, Employee e2)
{
if (e1.getSalary() < e2.getSalary()) return -1;
if (e1.getSalary() > e2.getSalary()) return 1;
else return 0;
}
}
So here we have a separate class, which implements Comparator interface.
Now this class is passed to Collections.sort method. We add 2 lines.
EmpCompare empCompare = new EmpCompare();
Collections.sort(list, empCompare);
and everything else remains same in the code which we saw above.
Benefit of using Comparator interface is that we can compare on various properties since the class is different for each comparison.
For comparable, the compareTo interface method has to be defined in the class to be compared. So it can be compared only on one property.
So comparable is compared using compareTo method and comparator is compared using compare method.
Generics is a way in which a single method can be called with arguments of different types i.e; different classes. For example if a method has only one argument, if we use generics on that argument then we can pass objects of any class as a parameter to that method. How we can do that we will show you below.
According to the parameter which was passed, the method will handle the execution appropriately.
Also if it is generic class, it can work with Objects with different classes. Let’s see how a generic class is created.
class GenericTest<T>
{
// An object of type T is declared
T obj;
//We declare the constructor below
GenericTest(T obj)
{
this.obj = obj;
}
//We fetch the object which was passed during invoking the constructor
public T getObject() {
return this.obj;
}
}
If you see above, class GenericTest called with generic type <T>.
Now let’s call the class or you can say instantiate the class object.
class Main
{
public static void main (String[] args)
{
GenericTest<String> genObject =
new GenericTest<String>("John");
System.out.println(genObject.getObject());
GenericTest<Integer> genObject =
new GenericTest<Integer>(100);
System.out.println(genObject.getObject());
}
}
If you see above, the class GenericTest is called with an String Type and then with an Integer Type.
The output is printed as:
John
100
So the generic class GenericTest can handle any type and give the output accordingly.
Now let’s take a look into a generic method.
class Test
{
public <T> void genericMethod (T obj)
{
System.out.println(obj.getClass().getName() +
" = " + obj);
}
public static void main(String[] args)
{
genericMethod(100);
genericMethod("John");
}
}
O/P:
Java.Lang.Integer = 100
Java.Lang.String = John
So the method handled both String and Integer types and gave the appropriate output. We can add additional types as well like double, BigDecimal etc.
***Remember one thing. Generics applies only for objects/reference types and not for primitives ****
Taking an example of collection classes we can have a generic class or generic method having generic type as List. By doing this, we can pass all it’s child classes like ArrayList, LinkedList, Vector etc to that method or Class and that should work perfectly.
But now let’s look at a problem which can occur if generics are not used.
Class GenericTest
{
public List populateList()
{
ArrayList list = new ArrayList();\\Line 5
list.add("James");//Line 7
list.add("Gosling");//Line 8
list.add("Gosling2"); //Line 9
list.add(100); //Line 10
return list;
}
public Static void main(Strng args[])
{
GenericTest genTest = new GenericTest();
ArrayList arrList = genTest.populateList();
for(String val : arrList) //Line 20
System.out.println("The List value is "+val);
}
}
Line 10, would not give any issue during compilation. But line 20, would fail during run time because we are mapping an integer 100 to a String variable.
To resolve this, we should change the ArrayList declaration in Line 5 to below ArrayList<String> arrList = new ArrayList<String>();
By doing this, while inserting 100 an Integer as seen in line 10 above, it would give an compile time error.
Benefits of Generic:
1)Type safety as seen above. String type would only include String values.
2)For one argument type, we can pass any child types against it. Like for type List, we can pass arguments with the types ArrayList,LinkedList etc.
To represent a sequence of characters, apart from string class there are 2 other classes available. But unlike String, those classes are mutable i.e; value of the string can be changed.
Those are StringBuffer and StringBuilder.
What is StringBuffer and StringBuilder?
StringBuffer is synchronized i.e. thread safe. It means two threads can’t call the methods of StringBuffer simultaneously.
StringBuilder is non-synchronized i.e. not thread safe. It means two threads can call the methods of StringBuilder simultaneously.
Since methods of StringBuffer are synchronized, it is slower than StringBuilder.
Let’s see an example.
public class StringTest
{
public static void main(String[] args){
StringBuilder builder=new StringBuilder("hello");
builder.append("John");
StringBuffer buffer=new StringBuffer("Hello");
buffer.append("David");
System.out.println("The builder is "+builder);
System.out.println("The buffer is "+buffer);
}
}