Multithreading software is made of many threads.
When executing a program this makes a process which is executed by the CPU.
Process: an instance of the execution of a program with its CPU context (registers, stack and so on), with its RAM memory (variables, instructions), and with its resources (input devices, output devices, file system etc)
Each process is independent from the others because it is referred to a different instance of the software.
Two processes cannot be executed in pipeline unless it’s used a multi core CPU. Each core can execute a single process.
One process can be made of multiple threads.
One thread is part of a process.
The threads of the same process can be related and dependent one from the others: One thread can wait till the second one finishes or two threads can use the same variables (RAM memory) and so on.
The advantage of the threads is that they can be executed in pipeline even with a single core CPU.
There are two ways to make a thread:
the first one is Implementing Runnable interface
public class RunnableClassConsumer implements Runnable{ @Override public void run() { for(int i=0;i<10;i++) { obj.setVariable(i); System.out.println("Consumer #" + this.number + " get: " + i); } } public static void main(String[] args) throws Exception { // TODO Auto-generated method stub SharedClassSynchro sharedClass=new SharedClassSynchro(); ThreadClassProdSyncro threadClassProd=new ThreadClassProdSyncro(sharedClass,1); ThreadClassConsumerSyncro threadClassConsumer=new ThreadClassConsumerSyncro(sharedClass,1); // RunnableClassConsumer runnableClassConsumer=new RunnableClassConsumer(sharedClass,1); threadClassProd.start(); // Thread threadClassConsumer=new Thread(runnableClassConsumer); threadClassConsumer.start(); } public static void main(String[] args) throws Exception { SharedClass sharedClass=new SharedClass(); ThreadClassProd threadClassProd=new ThreadClassProd(sharedClass,1); ThreadClassConsumer threadClassConsumer=new ThreadClassConsumer(sharedClass,1); // RunnableClassConsumer runnableClassConsumer=new RunnableClassConsumer(sharedClass,1); ExecutorService esecutore = Executors.newFixedThreadPool(1); esecutore.submit(threadClassProd); esecutore.submit(threadClassConsumer); esecutore.shutdown(); }
the runnable interface allows the developer to use the Executor that makes possible the use of definite number of threads, in this case, there is only one thread of productor and one thread of consumer
The other way to develop a multithreading application is by extending a Thread class
public class ThreadClassConsumer extends Thread{ private SharedClass obj; private int number; public SharedClass getObj() { return obj; } public void setObj(SharedClass obj) { this.obj = obj; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public void run() { for(int i=0;i<10;i++) { obj.setVariable(i); System.out.println("Consumer #" + this.number + " get: " + i); } } public ThreadClassConsumer(SharedClass obj,int number) { setObj(obj); setNumber(number); } }
The problem of Multithreading is the concurrency of data and the Deadlock: Two threads can use the same resource in the same time, this can produce an inconsistent state for that resource.
Avoiding this problem is possible by using Synchronized methods or resources that are used by one thread at a time sequentially and also by using Semaphore such as I’ve done below
public class SharedClassSynchro { private int variable; private boolean available=false; public synchronized int getVariable() { while (available==false) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } available=false; notifyAll(); return variable; } public synchronized void setVariable(int variable) { while (available==true) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } this.variable = variable; available=true; notifyAll(); } }