Threading in java
What is a thread?
A thread is a lightweight process that can run concurrently with other threads within the same program. Each thread has its own call stack and can execute code independently of other threads, allowing for parallelism and concurrency in programs.
How to create a thread in Java?
In Java, threads can be created by extending the Thread
class or implementing the Runnable
interface. Here is an example of how to create a thread by extending the Thread
class:
class MyThread extends Thread {
public void run() {
// code to be executed by the thread
}
}
To start the thread, we create an instance of the MyThread
class and call its start()
method:
MyThread t = new MyThread();
t.start();
When the start()
method is called, a new thread of execution is created and the run()
method of the MyThread
class is called. The run()
method contains the code that will be executed by the thread.
Here is an example of how to create a thread by implementing the Runnable
interface:
class MyRunnable implements Runnable {
public void run() {
// code to be executed by the thread
}
}
To start the thread, we create an instance of the MyRunnable
class and pass it to a new instance of the Thread
class:
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
When the start()
method is called on the Thread
object, a new thread of execution is created and the run()
method of the MyRunnable
class is called.
Synchronization and locks
When multiple threads access shared resources (such as variables or code segments) concurrently, there is a risk of race conditions and data corruption. To prevent this, we can use synchronization mechanisms such as locks.
In Java, the synchronized
keyword can be used to create synchronized blocks of code, which can only be executed by one thread at a time. Here is an example:
class MyCounter {
private int count;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
In this example, the increment()
and getCount()
methods of the MyCounter
class are marked as synchronized. This ensures that only one thread can execute these methods at a time, preventing race conditions and data corruption.
We can also use locks to synchronize access to shared resources. In Java, locks can be created using the Lock
interface and its implementation classes such as ReentrantLock
or ReadWriteLock
. Here is an example:
class MyCounter {
private int count;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
In this example, we create a ReentrantLock
object to synchronize access to the count
variable. The lock()
and unlock()
methods are called to acquire and release the lock, respectively. We use the try-finally
block to ensure that the lock is always released, even if an exception is thrown.