[go: up one dir, main page]

0% found this document useful (0 votes)
17 views14 pages

Thread + Concurrency

Java threads

Uploaded by

bhanduresanjana
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views14 pages

Thread + Concurrency

Java threads

Uploaded by

bhanduresanjana
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Concurrency

**Concurrency** refers to the ability to run multiple threads simultaneously, which can be
particularly useful for tasks that can be parallelized or that involve waiting (like I/O
operations). Java provides built-in support for concurrency using the **Thread** class,
**Runnable interface**, and higher-level utilities like **ExecutorService**.

Below is an example of how to create and run concurrent tasks in Java using different methods.

### Example 1: Using `Thread` Class

In this example, we extend the `Thread` class and override the `run()` method to define the
task that will be executed concurrently.

```
class MyThread extends Thread {
@Override
public void run() {
try {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
Thread.sleep(1000); // Simulate a task that takes 1 second
}
} catch (InterruptedException e) {
System.out.println(e);
}
}
}

public class ThreadExample {


public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();

t1.start(); // Starts thread t1


t2.start(); // Starts thread t2
}
}
```

**Explanation:**
- We create a `MyThread` class that extends `Thread` and overrides the `run()` method.
- Inside the `run()` method, we simulate a task by printing numbers with a 1-second delay using
`Thread.sleep(1000)`.
- Two instances of `MyThread` are created and started using the `start()` method, which will run
the threads concurrently.

### Example 2: Using `Runnable` Interface

Another way to implement concurrency in Java is by implementing the `Runnable` interface.


This approach is more flexible than extending `Thread` because a class can implement multiple
interfaces, whereas it can only extend one class.

```
class MyRunnable implements Runnable {
@Override
public void run() {
try {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
Thread.sleep(1000); // Simulate a task that takes 1 second
}
} catch (InterruptedException e) {
System.out.println(e);
}
}
}

public class RunnableExample {


public static void main(String[] args) {
MyRunnable task = new MyRunnable();

// Create two threads using the same Runnable object


Thread t1 = new Thread(task);
Thread t2 = new Thread(task);

t1.start(); // Starts thread t1


t2.start(); // Starts thread t2
}
}
```

**Explanation:**
- `MyRunnable` implements the `Runnable` interface and overrides the `run()` method.
- We create two threads `t1` and `t2` using the `Runnable` object (`task`).
- The threads run concurrently and execute the task defined in `run()`.

### Example 3: Using `ExecutorService` for Managing Threads


Instead of manually managing threads with `Thread` or `Runnable`, we can use the
`ExecutorService` framework to manage a pool of threads. This is more efficient and easier to
scale when dealing with many concurrent tasks.

```
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyTask implements Runnable {


@Override
public void run() {
try {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
Thread.sleep(1000); // Simulate a task that takes 1 second
}
} catch (InterruptedException e) {
System.out.println(e);
}
}
}

public class ExecutorServiceExample {


public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2); // Create a
thread pool with 2 threads

// Submit tasks to the executor service


executorService.submit(new MyTask());
executorService.submit(new MyTask());

executorService.shutdown(); // Shut down the executor service once tasks are done
}
}
```

**Explanation:**
- An `ExecutorService` is created using the `Executors.newFixedThreadPool(2)` method,
which creates a pool of 2 threads.
- We submit two tasks to the executor. These tasks will be run by the threads in the pool.
- After submitting the tasks, we call `shutdown()` to indicate that no more tasks will be
submitted.
### Example 4: Using `Callable` and `Future` for Return Values

If your concurrent tasks need to return a result or throw an exception, you can use the
`Callable` interface instead of `Runnable`. `Callable` allows tasks to return a result and is often
used with `Future` to get the result of the task.

```
import java.util.concurrent.*;

class MyCallable implements Callable<Integer> {


@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
Thread.sleep(1000); // Simulate a task that takes 1 second
}
return sum;
}
}

public class CallableExample {


public static void main(String[] args) throws InterruptedException, ExecutionException
{
ExecutorService executorService = Executors.newFixedThreadPool(2);

// Submit the callable task


Future<Integer> future1 = executorService.submit(new MyCallable());
Future<Integer> future2 = executorService.submit(new MyCallable());

// Get the result from the Future object


Integer result1 = future1.get(); // Blocks until the result is available
Integer result2 = future2.get(); // Blocks until the result is available

System.out.println("Result of task 1: " + result1);


System.out.println("Result of task 2: " + result2);

executorService.shutdown(); // Shut down the executor service


}
}
```

**Explanation:**
- We create a `MyCallable` class that implements the `Callable` interface. This interface allows
us to return a result (in this case, an integer sum).
- We submit the `MyCallable` tasks to an `ExecutorService`.
- We use the `get()` method of the `Future` object to retrieve the result of each task. This blocks
the main thread until the task completes.

### Example 5: Synchronized Block (Thread Safety)

When multiple threads access shared resources, you need to ensure thread safety. You can use
`synchronized` blocks or methods to prevent race conditions.

```
class Counter {
private int count = 0;

// Synchronized method ensures that only one thread can access it at a time
public synchronized void increment() {
count++;
}

public int getCount() {


return count;
}
}

public class SynchronizedExample {


public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();

// Create two threads that increment the counter


Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});

Thread t2 = new Thread(() -> {


for (int i = 0; i < 1000; i++) {
counter.increment();
}
});

t1.start();
t2.start();
t1.join();
t2.join();

System.out.println("Final count: " + counter.getCount());


}
}
```

**Explanation:**
- The `increment` method is marked as `synchronized`, ensuring that only one thread can
increment the `count` at a time.
- We create two threads that each increment the counter 1000 times.
- The `join()` method ensures that the main thread waits for both threads to finish before printing
the final count.

- **Thread** and **Runnable** are the basic building blocks for concurrency in Java.
- Use an **ExecutorService** for managing a pool of threads and tasks, which simplifies
thread management.
- **Callable** is useful when tasks need to return a result.
- **Synchronization** is necessary to ensure thread safety when accessing shared
resources.
Java Concurrency Interview Questions
Java concurrency assures us to do some tasks even faster by dividing these tasks
into subtasks. After that, these subtasks are executed synchronously.

These are some of the important Java concurrency questions which help
candidates to crack Java interviews.

1) Explain Countdownlatch in Java Concurrency.


The Countdownlatch is a synchronizer in Java that allows one thread to wait for
one or more threads before starting processing. It plays an important role in
server-side Java applications.

If one or more events are used in other threads, and we want to use those events
in the currently executing thread, we can push that thread in the waiting state by
using Countdownlatch until the threads that are using those events are not
complete.

2) Explain Cyclicbarrier in Java Concurrency.


Just like the Countdownlatch, the Cyclicbarrier concept is also used to set the
thread in the waiting state. The Cyclicbarrier sets the thread in the waiting state
for each other to reach a common barrier point. On the Cyclicbarrier object, we
need to call the await() method when each thread reaches the barrier.

3) Suppose a countdownlatch is initialized with 3, i.e., new


Countdownlatch(3). Is it required to have three threads for the
countdown?
It is not required to have three threads or we can say there is no need to have the
same number of threads for the countdown.

4) Explain Phaser in Java Concurrency.


Phaser plays an important role in the synchronization of threads over one or
more than one phase of activity. The Phaser acts like Cyclicbarrier when it is used
for synchronizing the single phase. It is very flexible and reusable in usage.

5) Explain Exchanger in Java Concurrency.


As the name suggests, it is related to exchanging something. Exchanger plays an
important role in exchanging data between two threads. Exchanger makes the
process easy for threads to share data between themselves. The exchanger
provides a point at which these threads can pair and swap elements, and that
point is called a synchronization point.

6) Explain Semaphore in Java Concurrency.


The Semaphore is a class that is present in the concurrent package of Java.util. It
is basically a semaphore that maintains a set of permissions.

The thread uses acquire() method for acquiring permits to get access to the
shared resource. At the same time, if the value of the Semaphore's count is not
equal to 0, the count value of the Semaphore will be decremented by one and
acquire a permit. Else the thread will be blocked until a permit is available. The
thread uses the release() method to release the shared resources when it is done
with it.

7) Explain Reentrantlock in Java Concurrency.


The ReentrantLock is basically a class that implements the Lock interface. When
a thread accesses the shared resources, it provides synchronization to methods.
On a resource, the ReentrantLock allows the thread to enter into the lock more
than once.

8) Explain Readwritelock in Java Concurrency.


The Readwritelock plays an important role in Java multi-threading applications.
In multi-threading applications, multiple read and write can occur
simultaneously for a shared resource. It is mainly used when two multiple writes
occur or 'read and write' occur at the same time. In the read and write case, there
will be a chance of writing and reading the wrong value. So, Readwritelock is
mainly used to boost the performance by locking either read or write operation.
9) Explain Reentrantreadwritelock in Java Concurrency.
It is an interface class that implements the ReadWriteLock interface and provides
us a read-write lock pair. We use the readWrite.readLock.lock() and
readWrite.writeLock.lock() methods to get read and write lock respectively. Here,
the readWrite is an instance of the Reentrantreadwritelock class.
ReentrantReadWriteLock also allows downgrading from write lock to read lock.

10) Explain Concurrenthashmap in Java Concurrency.


The concurrenthashmap is similar to the HashMap. The only difference between
HashMap and Concurrenthashmap is the locking strategy used by the
Concurrenthashmap. The Concurrenthashmap doesn't synchronize each method
on a common lock.

11) Explain Lock Striping in Java Concurrency.


The Lock Striping concept is used to separate locks for a portion of a data
structure where each lock is locking on a variable size set of independent objects.

12) Explain Copywritearraylist in Java Concurrency.


The Copywritearraylist is a class that implements the List interface. The main
difference between List and Copywritearraylist is that the Copywritearraylist is
thread-safe, but List is not. It provides better performance in scenarios where
there are more iterations of the lint than mutation.

13) Explain the difference between Copywritearraylist and


ArrayList in Java Concurrency.
The main difference b/w both of them is that the ArrayList is not thread-safe and
not good to use in a multi-threaded environment. The Copywritearraylist is
suitable to use in the multi-threaded environment because it is thread-safe.
Iterator returned by ArrayList and CopyOnWriteArrayList is fail-fast and fail-safe,
respectively.
14) Explain Copywritearrayset in Java Concurrency.
Another thread-safe collection that takes the support of CopyOnWriteArrayList to
perform all of its operations is known as Copywritearrayset. The Copywritearrayset
achieves thread-safety in the same way as the Copywritearraylist achieved
because the Copywritearraylist is used by the Copywritearrayset internally.

15) Explain Concurrentskiplistmap in Java Concurrency.


The Concurrentskiplistmap is a class that implements the
ConcurrentNavigableMap. The ConcurrentNavigableMap is similar to the
TreeMap with the added feature of being concurrent. It sorts the element based
on the natural order of keys.

16) Explain Callable in Java concurrency.


In Java, Callable is an interface that is available in Java5 or later versions. It is
mainly used to perform a task asynchronously by a thread. It provides a Call()
method which can return any type of value because the Callable interface is a
generic interface.

17) Explain Future in Java concurrency.


In Java, Future is also an interface that is mainly used to represent the outcome
of the asynchronous task. The Future object is returned by the Submit() method
of the ExecutorService. The future interface provides several methods for
checking whether the computation is complete or not, for waiting for its
completion, and for retrieving the result of the computation.

18) Explain Threadpool in Java concurrency.


Threadpool plays a very important role in Java concurrency. It is basically a
collection of worker threads that are waiting for the job and which we can use
again. In threadpool, a collection of threads(fixed size) are created, and a thread
from it is pulled out, and the service provider assigned a job to that thread.
19) Explain ExecutorService in Java concurrency.
The ExecutorService interface takes the support of the Executor interface and
allows us to manage termination and methods that return Future objects to
track the progress of more than one asynchronous task by providing several
methods.

The Submit method of the ExecutorService interface is more versatile compared


to others. The submit accepts not only runnable objects but also Callable objects
too.

20) Give the name of those classes which implement Executor


or ExecutortServiceinterface.
In Java, there are several classes that implement the Executor or ExecutorService
interface. Some of them are as follows:

1. The ThreadPoolExecutor is a class that takes the support of both the


interfaces for executing the submitted task by using one of the pooled
threads.
2. The ForkJoinPool takes the support of both interfaces. This class is mainly
used by the Fork/Join Framework.
3. The ScheduledThreadPoolExecutor takes the support of the
ThreadPoolExecutor class and the ScheduledExecutorService The
ScheduledThreadPoolExecutor class is mainly used for scheduling
commands to run after a given delay or to execute periodically.

21) Explain the difference between Submit() and Execute()


methods.
The Executor interface provides the execute() method. The execute() method
doesn't return anything and takes the Runnable command as an argument.

The ExecutorService interface provides the Submit() method, which is more


versatile compared to the execute() method. The Submit() method accepts not
only the Runnable interface but also the Callable objects too. Unlike execute()
method, the Submit() method returns a value or an object of type Future.
22) Explain ScheduledExecutorService.
The ScheduledExecutorService interface plays a very important role in Java
concurrency. This interface takes the support of the Executor interface and
provides methods for scheduling the command to execute periodically or to run
after a given time.

It provides the following two methods for executing the Callable and Runnable
task after a specified time period:

1. schedule(Callable<V> callable, long delay, TimeUnit unit)


2. schedule(Runnable command, long delay, TimeUnit unit)

23) Explain Executors class in Java concurrency.


The Executors class has several methods for the classes of the Executor
Framework such as ExecutorService, Executor, Callable,
ScheduledExecutorService, etc. We can directly use the ThreadPoolExecutor and
ScheduledThreadPoolExecutor, but it is not the best way. The best way of getting
an executor is to use the static factory methods of the utility class. These are
some of the following static factory methods:

1. The newCachedThreadPool() method

This method is mainly used for creating thread pools that use previously created
threads or create new ones as needed.

2. The newFixedThreadPool(int numThreads)

This method is also used for creating thread pools that use the fixed size of
previously created threads.

3. The newScheduledThreadPool(int numThreads)

This method is also used to create a thread pool for scheduling commands to
execute periodically or to run after a specified time.

4. The newSingleThreadExecutor()
This method is mainly used for creating an Executor. The created executor uses a
single worker thread operating off an unbounded queue.

24) Explain ConcurrentLinkedQueue in Java concurrency.


The ConcurrentLinkedQueue is a queue which is unbounded and thread-safe. It
stores the elements as linked nodes. It follows the concept of FIFO(First In First
Out).

25) Explain ConcurrentLinkedDequeue in Java concurrency.


Just like ConcurrentLinkedQueue, the ConcurrentLinkedDequeue is also an
unbounded thread-safe deque. It takes the support of the Deque interface, and
due to which, the insertion and deletion are possible from both ends.

26) Explain BlockingQueue in Java Concurrency.


The BlockingQueue interface is available in Java 5 or later. The BlockingQueue is
a queue that is mainly used to block operations. BlockingQueue supports only
those operations which are waiting for the queue.

BlockingQueue has the following two methods for blocking the operations.

1. The put(element) is used to insert the specified element into the queue.
2. The take() method is used to retrieve and remove the head from the
queue.

27) What are blocking methods in Java Concurrency?


Blocking methods are those methods which execute the assigned task without
relinquishing control to the other threads.

28) Explain LinkedBlockingQueue in Java Concurrency.


The LinkedBlockingQueue takes the support of the BlockingQueue interface,
and for storing elements, it uses linked nodes internally. Unlike
ArrayBlockingQueue, the LinkedBlockingQueue is optionally bound.
29) Explain PriorityBlockingQueue in Java Concurrency.
The PriorityBlockingQueue also takes the support of the BlockingQueue
interface and stores the elements in the same way as they are naturally ordered.
The order of elements is also based on the comparator, which is provided at
queue construction time.

30) Explain Synchronous and Delay queue in Java


Concurrency.
The Synchronous queue also takes the support of the BlockingQueue having no
internal capacity. In this queue, each 'insert' operation must wait for a
corresponding remove operation by another thread, and vice versa.

DelayQueue also takes the support of BlockingQueue and is an unbounded


queue interface. We can store only delayed types of elements and retrieve them
only when the delay has expired.

You might also like