diff --git a/.gitignore b/.gitignore
index a2d3d1f..60b502d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,92 @@
-# Add any directories, files, or patterns you don't want to be tracked by version control
-# Created by https://www.gitignore.io/api/Java
-# Edit at https://www.gitignore.io/?templates=Java
+
+# Created by https://www.gitignore.io/api/java,maven,intellij,sonar
+# Edit at https://www.gitignore.io/?templates=java,maven,intellij,sonar
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+.idea/modules.xml
+.idea/*.iml
+.idea/modules
+*.iml
+*.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+modules.xml
+.idea/misc.xml
+
+# Sonarlint plugin
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator/
### Java ###
# Compiled class file
@@ -27,4 +113,21 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
-# End of https://www.gitignore.io/api/Java
+### Maven ###
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+.mvn/wrapper/maven-wrapper.jar
+.flattened-pom.xml
+
+### Sonar ###
+#Sonar generated dir
+/.sonar/
+
+# End of https://www.gitignore.io/api/java,maven,intellij,sonar
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index a7b65c7..0000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,167 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1581865677580
-
-
- 1581865677580
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- file://$PROJECT_DIR$/src/com/oracle/java8/associate/InnerClassDemo.java
- 10
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OracleCertificationTest.iml b/OracleCertificationTest.iml
index ea1542a..22eec9e 100644
--- a/OracleCertificationTest.iml
+++ b/OracleCertificationTest.iml
@@ -1,7 +1,7 @@
-
+
diff --git a/src/com/oracle/java8/professional/ConcurrencyTest.java b/src/com/oracle/java8/professional/ConcurrencyTest.java
new file mode 100644
index 0000000..d96d04b
--- /dev/null
+++ b/src/com/oracle/java8/professional/ConcurrencyTest.java
@@ -0,0 +1,51 @@
+package com.oracle.java8.professional;
+
+import com.oracle.java8.professional.concurrency.BankAccount;
+import com.oracle.java8.professional.concurrency.Data;
+import com.oracle.java8.professional.concurrency.Receiver;
+import com.oracle.java8.professional.concurrency.Sender;
+
+public class ConcurrencyTest {
+
+ public static void main(String[] args) {
+ doBankAccoundDemo();
+ doSynchronizeThreadDemo();
+ }
+
+ private static void doBankAccoundDemo() {
+ BankAccount bankAccount = new BankAccount(1, 100);
+
+ Thread worker1 = new Thread(() -> {
+ System.out.printf("[Thread %s] read current balance = %d\n",
+ Thread.currentThread().getName(), bankAccount.getBalance());
+ bankAccount.deposit(10);
+ });
+
+ Thread worker2 = new Thread(() -> {
+ System.out.printf("[Thread %s] read current balance = %d\n",
+ Thread.currentThread().getName(), bankAccount.getBalance());
+ bankAccount.deposit(10000);
+ });
+
+ worker1.start();
+ worker2.start();
+
+ try {
+ worker1.join();
+ worker2.join();
+ System.out.println("Final balance is " + bankAccount.getBalance());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void doSynchronizeThreadDemo() {
+ Data data = new Data();
+ Thread sender = new Thread(new Sender(data));
+ Thread receiver = new Thread(new Receiver(data));
+
+ sender.start();
+ receiver.start();
+ }
+
+}
diff --git a/src/com/oracle/java8/professional/concurrency/BankAccount.java b/src/com/oracle/java8/professional/concurrency/BankAccount.java
new file mode 100644
index 0000000..7d67cc9
--- /dev/null
+++ b/src/com/oracle/java8/professional/concurrency/BankAccount.java
@@ -0,0 +1,19 @@
+package com.oracle.java8.professional.concurrency;
+
+public class BankAccount {
+ private long number;
+ private long balance;
+
+ public BankAccount(long number, long balance) {
+ this.number = number;
+ this.balance = balance;
+ }
+
+ public synchronized long getBalance() {
+ return balance;
+ }
+
+ public synchronized void deposit(long amout) {
+ balance += amout;
+ }
+}
diff --git a/src/com/oracle/java8/professional/concurrency/Data.java b/src/com/oracle/java8/professional/concurrency/Data.java
new file mode 100644
index 0000000..55453ce
--- /dev/null
+++ b/src/com/oracle/java8/professional/concurrency/Data.java
@@ -0,0 +1,40 @@
+package com.oracle.java8.professional.concurrency;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class Data {
+ private String packet;
+ private boolean transfer = true;
+
+ public synchronized void send(String packet) {
+ while (!transfer) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Logger.getAnonymousLogger().log(Level.SEVERE, "Interrupted error", e);
+ }
+ }
+
+ transfer = false;
+ this.packet = packet;
+ notifyAll();
+ }
+
+ public synchronized String receive() {
+ while (transfer) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Logger.getAnonymousLogger().log(Level.SEVERE, "Interrupted error", e);
+ }
+ }
+
+ transfer = true;
+ notifyAll();
+ return packet;
+ }
+
+}
diff --git a/src/com/oracle/java8/professional/concurrency/PingPong.java b/src/com/oracle/java8/professional/concurrency/PingPong.java
new file mode 100644
index 0000000..d1c53f4
--- /dev/null
+++ b/src/com/oracle/java8/professional/concurrency/PingPong.java
@@ -0,0 +1,29 @@
+package com.oracle.java8.professional.concurrency;
+
+public class PingPong extends Thread {
+ private String word;
+ private int delay;
+
+ public PingPong(String whatToSay, int delayTime) {
+ this.word = whatToSay;
+ this.delay = delayTime;
+ }
+
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ System.out.println(word + " ");
+ Thread.sleep(delay);
+ }
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+
+ public static void main(String[] args) {
+ System.out.println(Thread.currentThread().getName());
+ new PingPong("ping", 33).start();
+ new PingPong("PONG", 100).start();
+ }
+}
diff --git a/src/com/oracle/java8/professional/concurrency/Receiver.java b/src/com/oracle/java8/professional/concurrency/Receiver.java
new file mode 100644
index 0000000..ee7735b
--- /dev/null
+++ b/src/com/oracle/java8/professional/concurrency/Receiver.java
@@ -0,0 +1,31 @@
+package com.oracle.java8.professional.concurrency;
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class Receiver implements Runnable {
+ private Data load;
+
+ public Receiver(Data data) {
+ this.load = data;
+ }
+
+ public void run() {
+ for (String receivedMessage = load.receive();
+ !"End".equals(receivedMessage);
+ receivedMessage = load.receive()) {
+
+ System.out.println(receivedMessage);
+
+ // ...
+ try {
+ Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Logger.getAnonymousLogger().log(Level.SEVERE, "Interrupted error", e);
+ }
+ }
+ }
+
+}
diff --git a/src/com/oracle/java8/professional/concurrency/Sender.java b/src/com/oracle/java8/professional/concurrency/Sender.java
new file mode 100644
index 0000000..41c75f0
--- /dev/null
+++ b/src/com/oracle/java8/professional/concurrency/Sender.java
@@ -0,0 +1,37 @@
+package com.oracle.java8.professional.concurrency;
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class Sender implements Runnable {
+ private Data data;
+
+ public Sender(Data data) {
+ this.data = data;
+ }
+
+ @Override
+ public void run() {
+ String packets[] = {
+ "First packet",
+ "Second packet",
+ "Third packet",
+ "Fourth packet",
+ "End"
+ };
+
+ for (String packet : packets) {
+ data.send(packet);
+
+ // Thread.sleep() to mimic heavy server-side processing
+ try {
+ Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Logger.getAnonymousLogger().log(Level.SEVERE, "Interrupted error", e);
+ }
+ }
+ }
+
+}