Synchronization

This is Third Article in Series of Articles on Java 8 Concurrency Tutorial.

Synchronization

Threads communicate primarily by sharing access to fields and the objects reference fields refer to. This form of communication is extremely efficient, but makes two kinds of errors possible: thread interference and memory consistency errors. The tool needed to prevent these errors is synchronization.

There are many situations in which multiple threads must share access to common objects.
And There may be a situation when multiple threads try to access the same resource, Then they can produce inconsistent result due to concurrency issues.

e.g In below example two Threads are trying to increment counter by 1000, So after end of execution. Vlaue of counter should be 2000, but that not the case.

Inconsistent result due to concurrency - Without Synchronization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

public class SynchronizedMethodExample {

private static int counter= 0;
private static void increment()
{
counter = counter+1;
}

public static void main(String[] args) throws InterruptedException {

System.out.println("Main start!!");
Thread t1 = new Thread(new Runnable() {

public void run() {
for (int i = 0; i < 1000; i++) {
increment();

}

}
});

Thread t2 = new Thread(new Runnable() {

public void run() {
for (int i = 0; i < 1000; i++) {
increment();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Counter " + counter);
System.out.println("Main End");
}
}

If you check output , The value of Conter is not exactly equal to 2000.

Synchronization idioms

The Java programming language provides two basic synchronization idioms: synchronized methods and synchronized statements.

Synchronized Methods

To make a method synchronized, simply add the synchronized keyword to its declaration.
Synchronized method is used to lock an object for any shared resource. When a thread invokes a synchronized method, it automatically acquires the lock for that object and releases it when the thread completes its task.

If in above exapmle we make increment method as Synchronized, then has two effects:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

Synchronized Method Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

public class SynchronizedMethodExample {

private static int counter= 0;
private static synchronized void increment()
{
counter = counter+1;
}
public static void main(String[] args) throws InterruptedException {

System.out.println("Main start!!");
Thread t1 = new Thread(new Runnable() {

public void run() {
for (int i = 0; i < 1000; i++) {
increment();

}

}
});
Thread t2 = new Thread(new Runnable() {

public void run() {
for (int i = 0; i < 1000; i++) {
increment();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Counter " + counter);
System.out.println("Main End");
}
}

Synchronized Blocks

Each time We do not have to synchronize a whole method. Sometimes it is preferable to synchronize only part of a method. Java synchronized blocks inside methods makes this possible.The increment method Can implemented by using Synchronized Blocks

Synchronized Block Example
1
2
3
4
5
6
7

private void increment()
{
synchronized(this){
counter = counter+1;
}
}

It is better to use Synchronized Blocks using private object, rather than putting it on a method.

Putting it on the method means you are using the lock of the object itself to provide thread safety. With this kind of mechanism, it is possible for a malicious user of your code to also obtain the lock on your object, and hold it forever, effectively blocking other threads. A non-malicious user can effectively do the same thing inadvertently.

If you use the lock of a private data member, you can prevent this, since it is impossible for a malicious user to obtain the lock on your private object.

Synchronized Block Example
1
2
3
4
5
6
7
8
9

private final Object lockObject = new Object();

private void increment()
{
synchronized(lockObject){
counter = counter+1;
}
}
Share Comments

Join Method

This is Second Article in Series of Articles on Java 8 Concurrency Tutorial.

The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,

t.join();
causes the current thread to pause execution until t’s thread terminates.

Overloads of join allow the programmer to specify a waiting period. However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.

join responds to an interrupt by exiting with an InterruptedException

Join Method Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class JoinMethodExample {

public static void main(String[] args) {

System.out.println("Main Method Start");

Thread t1 = new Thread(()->System.out.println("Thread Number 1"));
Thread t2 = new Thread(()->System.out.println("Thread Number 2"));
t1.start();
t2.start();
System.out.println("Main Method End");
}
}

If you check output , The main Thread ends before T2 Thread. If you want to wait for Completion of T2 then we need to call join method.

Join Method Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public class JoinMethodExample {

public static void main(String[] args) {

System.out.println("Main Method Start");

Thread t1 = new Thread(()->System.out.println("Thread Number 1"));
Thread t2 = new Thread(()->System.out.println("Thread Number 2"));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Main Method End");
}
}
Share Comments

Creating Threads in Java

This is First Article in Series of Articles on Java 8 Concurrency Tutorial.

Threads can be Created using below ways.

Extending Thread class

The First way is to extend the Thread class, and override the run()
The extending class must override run() method which is the entry point of new thread.

Extending Thread class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

class ThreadRunner extends Thread
{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("ThreadRunner : " + i);
}
}

}

public class CreatingThreadsExample {

public static void main(String[] args) {

System.out.println("Main Method Start");

Thread t1= new ThreadRunner();
t1.start();
System.out.println("Main Method End");

}

}

Implementing the Runnable Interface

We Can pass an implementation of the Runnable interface to the constructor of Thread, then call start()

Implementing the Runnable Interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

class ThreadRunner implements Runnable
{
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("ThreadRunner1 : " + i);
}
}

}

public class CreatingThreadsExample {

public static void main(String[] args) {

System.out.println("Main Method Start");

Thread t1= new Thread(new ThreadRunner());
t1.start();
System.out.println("Main Method End");

}

}

Threads Using Anonymous Classes

Anonymous Inner class is an inner class that is declared without any class name and that’s why it’s called anonymous. You can define an anonymous inner class within a method or even within an argument to a method.

Anonymous class can be used to -
Extend an class and override its method.
Implement an interface and provide an implementation of its method.

Threads Using Anonymous Classes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CreatingThreadsExample {

public static void main(String[] args) {

System.out.println("Main Method Start");

new Thread(new Runnable() {

public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("ThreadRunner : " + i);
}

}
}).start();
}
}

Threads Using Java 8 Lambda

Runnable is a functional interface and we can use lambda expressions to provide it’s implementation rather than using anonymous class.

Threads Using Anonymous Classes
1
2
3
4
5
6
7
8
9
10
public class CreatingThreadsExample {

public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 100; i++) {
System.out.println("ThreadRunner2 : " + i);
} };
new Thread(task).start();
}
}

Next Join Method. in Series of Articles on Java 8 Concurrency Tutorial.

Share Comments

Java 8 Concurrency Tutorial

Welcome to Java Concurrency tutorials. These articles will describe you the Java Concurrency concepts in the context of Java 8 with easily understood code examples.

The majority of concepts shown in these articles are also available in older versions of Java.
However, my code samples focus on Java 8 and make heavy use of lambda expressions and other new features.

Topics

  1. Creating Threads in Java
  2. Join Method
  3. Synchronization
  4. Intrinsic Locks
  5. Volatile
  6. Wait-Notify-And-Notifyall
  7. ExecutorServiceAndThreadPools
  8. Callable and Future
  9. Semaphores
  10. CountDownLatch
  11. CyclicBarrier
  12. BlockingQueue
  13. Exchanger

Check The First Article in Series of Java Concurrency tutorials Creating Threads in Java.

Share Comments

Watching a Directory for Changes

The java.nio.file package provides a file change notification API, called the Watch Service API.

This API enables you to register a directory (or directories) with the watch service. When registering, you tell the service which types of events you are interested in:
1:File creation.
2:File deletion.
3:File Modification.

When the service detects an event of interest, it is forwarded to the registered process.

The registered process has a thread (or a pool of threads) dedicated to watching for any events it has registered for. When an event comes in, it is handled as needed.

Creating watcher service

The first step is to create a new WatchService by using the newWatchService method in the FileSystem class, as follows:

WatchService watcher = FileSystems.getDefault().newWatchService()

Registering for Events

We Can register one or more objects with the watch service.Any object that implements the Watchable interface can be registered.
The Path class implements the Watchable interface, so each directory to be monitored is registered as a Path object.

When registering an object with the watch service, you specify the types of events that you want to monitor. The supported StandardWatchEventKinds event types follow:

  1. ENTRY_CREATE – A directory entry is created.
  2. ENTRY_DELETE – A directory entry is deleted.
  3. ENTRY_MODIFY – A directory entry is modified.
Registering for Events
1
2
3
WatchService watcher = FileSystems.getDefault().newWatchService()
Path dir = Paths.get("C:\\data\\temp\\mydir\\");
dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

Directory Watching Example

Putting all above together. We can now go ahead and look at a complete and practical example.

In below example we are going to watch directory for all the changes and will process the events.

Directory Watching Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;


public class DirectoryChangeListeners {

public static void main(String[] args) throws InterruptedException {
try {
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("C:\\data\\temp\\");
dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
System.out.println("Watch Service registered for dir: " + dir.getFileName());
WatchKey key;
while ((key = watcher.take())!=null)
{
for (WatchEvent<?> event : key.pollEvents()) {

WatchEvent.Kind<?> kind = event.kind();

@SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path fileName = ev.context();

if(kind==ENTRY_CREATE)
{
System.out.println("New File Added, file Name " + fileName);
}
if(kind==ENTRY_DELETE)
{
System.out.println("File Deleted " + fileName);
}

if (kind == ENTRY_MODIFY ) {
System.out.println("File Modified " + fileName);
}
}

boolean valid = key.reset();
if (!valid) {
break;
}
}

} catch (IOException ex) {
System.err.println(ex);
}
}
}

Key Points

Three methods are available for Retrieving events :
  1. poll – Returns a queued key, if available. Returns immediately with a null value, if unavailable.
  2. poll(long, TimeUnit) – Returns a queued key, if one is available. If a queued key is not immediately available, the program waits until the specified time. The TimeUnit argument determines whether the specified time is nanoseconds, milliseconds, or some other unit of time.
  3. take – Returns a queued key. If no queued key is available, this method waits.
Reset key

After the events for the key have been processed, you need to put the key back into a ready state by invoking reset. If this method returns false, the key is no longer valid and the loop can exit. This step is very important. If you fail to invoke reset, this key will not receive any further events.

When to Use and Not Use This API

The Watch Service API is designed for applications that need to be notified about file change events. It is well suited for any application, like an editor or IDE, that potentially has many open files and needs to ensure that the files are synchronized with the file system. It is also well suited for an application server that watches a directory, perhaps waiting for .jsp or .jar files to drop, in order to deploy them.

This API is not designed for indexing a hard drive. Most file system implementations have native support for file change notification. The Watch Service API takes advantage of this support where available. However, when a file system does not support this mechanism, the Watch Service will poll the file system, waiting for events.

Share Comments

Java 8 File Operations - Copy,Delete,Move

Deleting a File or Directory

The Files class provides two deletion methods.

1 : The delete(Path) method deletes the file or throws an exception if the deletion fails

2 : The deleteIfExists(Path) method also deletes the file, but if the file does not exist, no exception is thrown.

Delete File
1
2
3
4
5
6
7
8
9
10
11
12
13

public static void main(String[] args) {

Path path = Paths.get("C:\\data\\temp\\temp.txt");
try {
Files.delete(path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

Delete Empty Directory
1
2
3
4
5
6
7
8
9
10
11
12
13

public static void main(String[] args) {

Path path = Paths.get("C:\\data\\temp\\");
try {
Files.delete(path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

Trying to delete Non Empty Directory will throw DirectoryNotEmptyException.
So, First we need to delete all files inside a particular non-empty directory:

Delete Non Empty Directory
1
2
3
4
5
6
7
8
9
10
11

public static void main(String[] args) {

Path path = Paths.get("C:\\data\\temp\\");
Files.walk(path)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);

}

Copying a File or Directory

You can copy a file or directory by using the
copy(Path, Path, CopyOption...) method. The copy fails if the target file exists, unless the REPLACE_EXISTING option is specified.

This method takes a varargs argument. The following StandardCopyOption and LinkOption enums are supported:

REPLACE_EXISTING – replace a file if it exists
COPY_ATTRIBUTES – copy metadata to the new file
NOFOLLOW_LINKS – shouldn’t follow symbolic links

Copy File
1
2
3
4
5
6
7
8

public static void main(String[] args) {
Path sourcepath = Paths.get("C:\\data\\temp\\temp.txt");
Path destinationepath = Paths.get("C:\\data\\temp\\destination.txt");
Files.copy(sourcepath, destinationepath, StandardCopyOption.REPLACE_EXISTING);

}

Copy Empty Directory
1
2
3
4
5
6
7
8

public static void main(String[] args) {
Path sourcepath = Paths.get("C:\\data\\temp\\mydir");
Path destinationepath = Paths.get("C:\\data\\temp\\destinationDir");
Files.copy(sourcepath, destinationepath, StandardCopyOption.REPLACE_EXISTING);

}

Copy Non empty Directory
Directories can be copied. However, files inside the directory are not copied, so the new directory is empty even when the original directory contains files.

Copy Non Empty Directory recursively
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public static void main(String[] args) {
Path sourcepath = Paths.get("C:\\data\\temp\\mydir");
Path destinationepath = Paths.get("C:\\data\\temp\\destinationDir");
Files.walk(sourcepath)
.forEach(source -> copy(source, destinationepath.resolve(sourcepath.relativize(source))));
}

static void copy(Path source, Path dest) {
try {
Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}

Moving a File or Directory

You can move a file or directory by using the move(Path, Path, CopyOption...) method.
The move fails if the target file exists, unless the REPLACE_EXISTING option is specified.

Empty directories can be moved. If the directory is not empty, the move is allowed when the directory can be moved without moving the contents of that directory. On UNIX systems, moving a directory within the same partition generally consists of renaming the directory. In that situation, this method works even when the directory contains files.

This method takes a varargs argument – the following StandardCopyOption enums are supported:

REPLACE_EXISTING – Performs the move even when the target file already exists. If the target is a symbolic link, the symbolic link is replaced but what it points to is not affected.
ATOMIC_MOVE – Performs the move as an atomic file operation. If the file system does not support an atomic move, an exception is thrown. With an ATOMIC_MOVE you can move a file into a directory and be guaranteed that any process watching the directory accesses a complete file.

Move File
1
2
3
4
5
6
7
8

public static void main(String[] args) {
Path sourcepath = Path sourcepath = Paths.get("C:\\data\\temp\\temp.txt");
Path destinationepath = Paths.get("C:\\data\\temp\\mydir\\temp.txtr");

Files.move(sourcepath, destinationepath, StandardCopyOption.REPLACE_EXISTING);

}
Move Empty Directory
1
2
3
4
5
6
7
8

public static void main(String[] args) {
Path sourcepath = Paths.get("C:\\data\\temp\\copyme");
Path destinationepath = Paths.get("C:\\data\\temp\\mydir\\copyme");
Files.move(sourcepath, destinationepath, StandardCopyOption.REPLACE_EXISTING);


}
Move Non Empty Directory
1
2
3
4
5
6
7

public static void main(String[] args) {
Path sourcepath = Paths.get("C:\\data\\temp\\copyme");
Path destinationepath = Paths.get("C:\\data\\temp\\mydir\\copyme");
Files.move(sourcepath, destinationepath, StandardCopyOption.REPLACE_EXISTING);

}
Share Comments

Java 8 List all Files in Directory and Subdirectories

List All Files in Directory and Subdirectories

Files.walk Return a Stream that is lazily populated with Path by walking the file tree rooted at a given starting file.

Files.list Method Return a lazily populated Stream for the current directory only,Files.walk can be used to get list of files from Directory & Subdirectories .

Example 1: List All Files in Directory and Subdirectories
List All Files in Directory and Subdirectories
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public static void main(String[] args) throws IOException {

Path start = Paths.get("C:\\data\\");
try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
List<String> collect = stream
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());

collect.forEach(System.out::println);
}


}

Note

Files.walk method takes int maxDepth as parameter. The maxDepth parameter is the maximum number of levels of directories to visit.
MAX_VALUE may be used to indicate that all levels should be visited. Value 1 can be used to list files in current Directory.

Example 2: List All Files in Current Directory only
List All Files in Current Directory only
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public static void main(String[] args) throws IOException {

Path start = Paths.get("C:\\data\\");
try (Stream<Path> stream = Files.walk(start, 1)) {
List<String> collect = stream
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());

collect.forEach(System.out::println);
}


}

Share Comments

Java 8 Read File With try-with-resources

You might have noticed that In the previous post about files we have not closed any file stream. Streams implement AutoCloseable and in this case, we need to close stream explicitly. We can use try-with-resources to close the stream.

Sample Code

Close BufferedReader
1
2
3
4
5
6
7
8
9
10
11
12

public static void main(String[] args) throws IOException {
String filePath = "C:\\data\\demo\\sample.txt";
try(BufferedReader reader = Files.newBufferedReader(Paths.get(filePath)))
{
reader.lines().forEach(System.out::println);
}
catch (Exception e) {
// TODO: handle exception
}
}

Close Stream
1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws IOException {
String filePath = "C:\\data\\demo\\sample.txt";
try(Stream<String> lines = Files.lines(Paths.get((filePath))))
{
lines.forEach(System.out::println);

}
catch (Exception e) {
// TODO: handle exception
}
}
Share Comments

Java 8 List All Files in Directory

List All Files in Directory

Files.list Method Return a lazily populated Stream, the elements of which are the entries in the directory.

We Can use the stream operations to find Specific Files, List file matching certain criteria, List filenames in sorted order etc.

Example 1: List All Files in Directory
List All Files in Directory
1
2
3
4
5
6
7
8

public static void main(String[] args) throws IOException {

try(Stream<Path> list = Files.list(Paths.get("C:\\Program Files\\"));)
{
list.forEach(System.out::println);
}
}
Example 2: List All Files in Directory Starting with A
List All Files in Directory Starting with A
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public static void main(String[] args) throws IOException {

try (Stream<Path> list = Files.list(Paths.get("C:\\Program Files\\"))) {
List<String> fileList = list.map(path -> path.getFileName()
.toString())
.filter(name -> name.startsWith("A"))
.sorted()
.collect(Collectors.toList());
fileList.forEach(System.out::println);
}

}

Example 3: List Files Only
List Files Only
1
2
3
4
5
6
7
8
9
10
11
12

public static void main(String[] args) throws IOException {

try (Stream<Path> list = Files.list(Paths.get("C:\\Program Files\\"))) {
List<String> fileList = list.filter(path->path.toFile().isFile())
.map(path -> path.getFileName().toString())
.collect(Collectors.toList());
fileList.forEach(System.out::println);
}

}

Example 4: List Directory Only
List Directory Only
1
2
3
4
5
6
7
8
9
10
11
12

public static void main(String[] args) throws IOException {

try (Stream<Path> list = Files.list(Paths.get("C:\\Program Files\\"))) {
List<String> fileList = list.filter(path->path.toFile().isDirectory())
.map(path -> path.getFileName().toString())
.collect(Collectors.toList());
fileList.forEach(System.out::println);
}

}

Example 5: List Hidden files Only
List Hidden files Only
1
2
3
4
5
6
7
8
9
10
11
12

public static void main(String[] args) throws IOException {

try (Stream<Path> list = Files.list(Paths.get("C:\\Program Files\\"))) {
List<String> fileList = list.filter(path->path.toFile().isHidden())
.map(path -> path.getFileName().toString())
.collect(Collectors.toList());
fileList.forEach(System.out::println);
}

}

Note

Files.list Method Return a lazily populated Stream for the directory.
It does not return Stream for the nested directory. For that, we Can use File.walk . Will discuss that in next chapter.

Share Comments

Java 8 Read File Using Buffered Reader

Finally, Java 8 has made Reading & Writing a text file a simple task.

If we need more fine-grained control on reading we can use new Files.newBufferedReader()

Read File Using Buffered Reader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class Java8ReadUsingBufferedReader {

public static void main(String[] args) throws IOException {
String filePath = "C:\\data\\demo\\sample.txt";
BufferedReader reader = Files.newBufferedReader(Paths.get(filePath));
reader.lines().forEach(System.out::println);

}

}
Sample.txt file
1
2
3
4
5
6
7
public final class Files extends Object
This class consists exclusively of static methods that operate on files, directories, or other types of files.
In most cases, the methods defined here will delegate to the associated file system provider to perform the file operations.

Since:
1.7

Share Comments