public class MyThread {

	public static void main(String[] args) {

		Thread t = new Thread() {
			@Override
			public void run() {
				System.out.println("thread started");
				for (int i = 0; i < 10; i++) {
					System.out.println("thread working : " + i);
				}
				System.out.println("thread stopped");
			}
		};

		t.start();
	}

}

public class MyRunnable {

	public static void main(String[] args) {

		Runnable r = () -> {
			System.out.println("thread started");
			for (int i = 0; i < 10; i++) {
				System.out.println("thread working : " + i);
			}
			System.out.println("thread stopped");
		};

		Thread t = new Thread(r);
		t.start();

	}

}

public class StackMain {

	public static void main(String[] args) {
		method1();
		System.out.println(Thread.currentThread() + " : End main");
	}

	private static void method1() {

		Runnable r = () -> {
			System.out.println(Thread.currentThread() + " : task running");
		};

		Thread t = new Thread(r, "Sonic");
		t.run();
		t.start();
	}

}

public class ThreadPriority {

	public static void main(String[] args) {

		Runnable r = () -> {
			System.out.println(Thread.currentThread().getName() + " thread started");
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + " thread working : " + i);
			}
			System.out.println(Thread.currentThread().getName() + " thread stopped");
		};

		Thread t1 = new Thread(r, "t1");
		t1.setPriority(2);

		Thread t2 = new Thread(r, "t2");
		t2.setPriority(Thread.MAX_PRIORITY);

		Thread t3 = new Thread(r, "t3");
		t3.setPriority(Thread.MIN_PRIORITY);

		Thread t4 = new Thread(r, "t4");
		t4.setPriority(Thread.NORM_PRIORITY);

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

public class AccountDemo implements Runnable {

	private Account account = new Account();

	public static void main(String[] args) {

		AccountDemo r = new AccountDemo();

		Thread t1 = new Thread(r, "Alice");
		Thread t2 = new Thread(r, "Bob");
		t1.start();
		t2.start();
	}

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			makeWithdrawal(20);
			if (account.getBalance() < 0) {
				System.out.println("account is overdrawn!");
			}
		}
	}

	private void makeWithdrawal(int amount) {

		if (account.getBalance() >= amount) {
			System.out.println(Thread.currentThread().getName() + " is going to withdraw");

			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
			}

			account.withdraw(amount);
			System.out.println(Thread.currentThread().getName() + " completes the withdrawal");
		} else {
			System.out.println(
					"Not enough in account for " + Thread.currentThread().getName() + " to withdraw " + account.getBalance());
		}
	}

	private class Account {

		private int balance = 100;

		int getBalance() {
			return balance;
		}

		void withdraw(int amount) {
			this.balance = this.balance - amount;
		}
	}

}

private synchronized void makeWithdrawal(int amount) {

	if (account.getBalance() >= amount) {
		System.out.println(Thread.currentThread().getName() + " is going to withdraw");

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
		}

		account.withdraw(amount);
		System.out.println(Thread.currentThread().getName() + " completes the withdrawal");
	} else {
		System.out.println(
				"Not enough in account for " + Thread.currentThread().getName() + " to withdraw " + account.getBalance());
	}
}

public void doAction() {

	// do something by multiple threads

	synchronized (this) {
		// critical session executed by one thread only
	}

	// continue do something by multiple threads
}

public void doAction() {
	synchronized (this) {
		// do something by one thread
	}
}

private final Object lock = new Object();

public void doAction() {

	// do something by multiple threads

	synchronized (lock) {
		// critical session executed by one thread only
	}

	// continue do something by multiple threads
}

private static final Object LOCK = new Object();

public void doAction() {

	// do something by multiple threads

	synchronized (LOCK) {
		// critical session executed by one thread only
	}
	// continue do something by multiple threads

private void doAction() {
	synchronized (MyClass.class) {
		// critical session executed by one thread only
	}
}

public class SynchronizationSummary {

	private final Object lockA = new Object();

	private final Object lockB = new Object();

	private static final Object LOCK_C = new Object();

	public void doAction1() {
		synchronized (lockA) {
			// critical session executed by one thread only
		}
	}

	public void doAction2() {
		synchronized (lockB) {
			// critical session executed by one thread only
		}
	}

	public synchronized void doAction3() {
		// critical session executed by one thread only
	}

	public static synchronized void doAction4() {
		// critical session executed by one thread only
	}

	public void doAction5() {
		synchronized (LOCK_C) {
			// critical session executed by one thread only
		}
	}

	public void doAction6() {
		synchronized (SynchronizationSummary.class) {
			// critical session executed by one thread only
		}
	}
}

public class NonAtomicOperation {

	private List<String> names = Collections.synchronizedList(new LinkedList<String>());
	
	private void add(String name) {
		names.add(name);
	}

	private String removeFirst() {
		if (!names.isEmpty()) {
			return names.remove(0);
		} else {
			return null;
		}
	}

	public static void main(String[] args) {

		final NonAtomicOperation n1 = new NonAtomicOperation();
		n1.add("Foobar");

		Runnable r = () -> {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// nothing to do
			}
			String name = n1.removeFirst();
			System.out.println(name);
		};

		Thread t1 = new Thread(r, "Thread-1");
		Thread t2 = new Thread(r, "Thread-2");
		t1.start();
		t2.start();
}

public class ProducerConsumerThreadSafe {

	private static final int MAX_ELEMENTS = 20;

	private static final int BUFFER_SIZE = 5;

	private Queue<Integer> queue = new LinkedList<>();

	private final Object lock = new Object();

	public static void main(String[] args) {

		ProducerConsumerThreadSafe main = new ProducerConsumerThreadSafe();
		main.runDemo();
	}

	private void runDemo() {

		Runnable producer = () -> {

			for (int i = 0; i < MAX_ELEMENTS; i++) {

				synchronized (lock) {
					while (queue.size() == BUFFER_SIZE) {
	System.out.println(Thread.currentThread().getName() + " : Queue is full. Waiting for produce ...");
						try {
							lock.wait();
						} catch (InterruptedException e) {
						}
					}
				}

				synchronized (lock) {
					System.out.println(Thread.currentThread().getName() + " put : " + i);
					queue.offer(i);
					lock.notify();
				}

			}
		};

		Runnable consumer = () -> {
			int counter = 0;

			while (counter < MAX_ELEMENTS) {

				synchronized (lock) {
					while (queue.isEmpty()) {
	System.out.println(Thread.currentThread().getName() + " : Queue is empty. Waiting for data to consume ...");
						try {
							lock.wait();
						} catch (InterruptedException e) {
						}
					}
				}

				synchronized (lock) {
	System.out.println(Thread.currentThread().getName() + " get : " + queue.poll());
					lock.notify();
					counter++;
				}

			}
		};

		Thread threadProducer = new Thread(producer, "Producer");
		Thread threadConsumer = new Thread(consumer, "Consumer");

		threadProducer.start();
		threadConsumer.start();
	}

}

private final Lock lock = new ReentrantLock();

public void doAction() {

	// do something by multiple threads

	try {
		lock.lock();
		// critical session executed by one thread only
	} finally {
		lock.unlock();
	}

	// continue do something by multiple threads
}

private final Lock lock = new ReentrantLock();

public void doAction() {

	// do something by multiple threads

	if (lock.tryLock()) {
		try {
			// critical session executed by one thread only
		} finally {
			lock.unlock();
		}
	} else {
		// do something else by multiple threads
	}
	// continue do something by multiple threads
}

private final Lock lock = new ReentrantLock();

public void doAction() {

	// do something by multiple threads

	if (lock.tryLock(2, TimeUnit.SECONDS)) {
		try {
			// critical session executed by one thread only
		} finally {
			lock.unlock();
		}
	} else {
		// do something else by multiple threads
	}
	// continue do something by multiple threads
}

public class ProducerConsumerThreadSafeJava5 {

	private static final int MAX_ELEMENTS = 20;

	private static final int BUFFER_SIZE = 5;

	private Queue<Integer> queue = new LinkedList<>();

	private final Lock lock = new ReentrantLock();

	public static void main(String[] args) {

		ProducerConsumerThreadSafeJava5 main = new ProducerConsumerThreadSafeJava5();
		main.runDemo();
	}

	private void runDemo() {

		final Condition conditionBufferEmpty = lock.newCondition();
		final Condition conditionBufferFull = lock.newCondition();

		Runnable producer = () -> {

			Thread.currentThread().setName("Producer");

			for (int i = 0; i < MAX_ELEMENTS; i++) {

				try {
					lock.lock();
					while (queue.size() == BUFFER_SIZE) {
	System.out.println(Thread.currentThread().getName() + " : Queue is full. Waiting for produce ...");
						try {
							conditionBufferFull.await();
						} catch (InterruptedException e) {
						}
					}
				} finally {
					lock.unlock();
				}

				try {
					lock.lock();
	System.out.println(Thread.currentThread().getName() + " put : " + i);
					queue.offer(i);
					conditionBufferEmpty.signal();
				} finally {
					lock.unlock();
				}

			}
		};

		Runnable consumer = () -> {
			int counter = 0;
			Thread.currentThread().setName("Consumer");

			while (counter < MAX_ELEMENTS) {

				try {
					lock.lock();
					while (queue.isEmpty()) {
	System.out.println(Thread.currentThread().getName() + " : Queue is empty. Waiting for data to consume ...");
						try {
							conditionBufferEmpty.await();
						} catch (InterruptedException e) {
						}
					}
				} finally {
					lock.unlock();
				}

				try {
					lock.lock();
	System.out.println(Thread.currentThread().getName() + " get : " + queue.poll());
					conditionBufferFull.signal();
					counter++;
				} finally {
					lock.unlock();
				}

			}
		};

		ExecutorService executor = Executors.newCachedThreadPool();
		executor.execute(producer);
		executor.execute(consumer);

		executor.shutdown();
	}

}

public class ProducerConsumerUsingBlockingQueue {

	private static final int MAX_ELEMENTS = 20;

	private static final int BUFFER_SIZE = 5;

	private BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(BUFFER_SIZE);

	public static void main(String[] args) {

		ProducerConsumerUsingBlockingQueue main = new ProducerConsumerUsingBlockingQueue();
		main.runDemo();
	}

	private void runDemo() {

		Runnable producer = () -> {

			Thread.currentThread().setName("Producer");

			for (int i = 0; i < MAX_ELEMENTS; i++) {

				try {
	System.out.println(Thread.currentThread().getName() + " put : " + i);
					queue.put(i);
				} catch (InterruptedException e) {
					// nothing to do
				}
			}

		};

		Runnable consumer = () -> {
			int counter = 0;
			Thread.currentThread().setName("Consumer");

			while (counter < MAX_ELEMENTS) {

				try {
	System.out.println(Thread.currentThread().getName() + " get : " + queue.take());
					counter++;
				} catch (InterruptedException e) {
					// nothing to do
				}
			}
		};

		ExecutorService executor = Executors.newCachedThreadPool();
		executor.execute(producer);
		executor.execute(consumer);

		executor.shutdown();
	}
}

public class DeadlockMain {

	private static final int COMPUTE_HARD = 1000;

	public static void main(String[] args) {

		final Runner runner = new Runner();

		Runnable r1 = () -> {
			try {
				runner.firstThread();
				System.out.println("Thread 1 is finished");
			} catch (InterruptedException e) {
			}
		};

		Runnable r2 = () -> {
			try {
				runner.secondThread();
				System.out.println("Thread 2 is finished");
			} catch (InterruptedException e) {
			}
		};

		Thread t1 = new Thread(r1, "Thread-1");
		Thread t2 = new Thread(r2, "Thread-2");

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

		try {
			t1.join();
			t2.join();
			runner.finished();
		} catch (InterruptedException e) {
		}
	}

	private static class Runner {

		private Account account1 = new Account();

		private Account account2 = new Account();

		private final Object lock1 = new Object();

		private final Object lock2 = new Object();

		public void firstThread() throws InterruptedException {

			Random random = new Random();

			for (int i = 0; i < COMPUTE_HARD; i++) {

				synchronized (lock1) {
					synchronized (lock2) {
						try {
							// transfer from 1 to 2
							int amount = random.nextInt(100);
							Account.transfer(account1, account2, amount);
							System.out.println(Thread.currentThread().getName()
									+ " completes the transaction from account1 to account2 of " + amount + " EUR");
							TimeUnit.MILLISECONDS.sleep(100);
						} finally {
						}
					}
				}
			}

		}

		public void secondThread() throws InterruptedException {

			Random random = new Random();

			for (int i = 0; i < COMPUTE_HARD; i++) {
				transferCredits(random);
			}
		}

		private void transferCredits(Random random) throws InterruptedException {
			// locks must be held and released in same order that in others threads
			// otherwise deadlock happens
			synchronized (lock2) {
				synchronized (lock1) {
					try {
						// transfer from 2 to 1
						int amount = random.nextInt(100);
						Account.transfer(account2, account1, amount);
						System.out.println(Thread.currentThread().getName()
								+ " completes the transaction from account2 to account1 of " + amount + " EUR");
						TimeUnit.MILLISECONDS.sleep(100);
					} finally {
					}
				}
			}
		}

		public void finished() {
			System.out.println("Account 1 balance : " + account1.getBalance());
			System.out.println("Account 2 balance : " + account2.getBalance());
			System.out.println("Total of accounts balances : " + (account1.getBalance() + account2.getBalance()));
		}
	}

	private static class Account {
		private int balance = 10000;

		public void deposit(int amount) {
			balance += amount;
		}

		public void withDraw(int amount) {
			balance -= amount;
		}

		public int getBalance() {
			return balance;
		}

		public static void transfer(Account acc1, Account acc2, int amount) {
			acc1.withDraw(amount);
			acc2.deposit(amount);
		}
	}

}

public class LivelockDiskSpaceMain {

	public static void main(String[] args) {

		DiskManager diskManager = new DiskManager();

		Thread t1 = new Thread(() -> {

			while (true) {
				if (diskManager.isTooLow()) {
					diskManager.reclaimSpace();
					sleep(500);
				}
				sleep(100);
			}
		}, "diskCleanerTask-t1");

		Thread t2 = new Thread(() -> {

			String name = Thread.currentThread().getName();
			int applicationSize = 60;
			int packSize = 10;
			int maxPart = applicationSize / packSize;
			int progressBar = 0;
			int currentSize = 0;
			while (currentSize < applicationSize) {

				sleep(500);
				try {
					int amountDone = diskManager.useSpace(packSize);
					currentSize = currentSize + amountDone;
					progressBar++;
					System.out.println(name + " progressing " + progressBar + "/" + maxPart);
				} catch (RuntimeException e) {
					System.out.println(name + " insufficient space. Download aborted. Retry in a few moments");
					progressBar = 0;
					currentSize = 0;
					sleep(500);
				}
			}
			System.out.println("End download");
		}, "downloader-t2");

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

		try {
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private static void sleep(long timeout) {
		try {
			TimeUnit.MILLISECONDS.sleep(timeout);
		} catch (InterruptedException e) {
		}
	}

	private static class DiskManager {
		private static final int MAX_THRESHOLD = 80;
		private static final int MAX_SPACE = 100;
		private static final int AMOUNT_TO_FREE = 4;
		private int usedSpace = 50;

		private synchronized void reclaimSpace() {
			usedSpace = usedSpace - AMOUNT_TO_FREE;
			System.out.println(Thread.currentThread().getName() + " space after clean : " + usedSpace);
		}

		private synchronized int useSpace(int amount) {
			if (isTooLow()) {
				System.out.println(Thread.currentThread().getName() + " no more available space");
				throw new RuntimeException("Space disk almost full");
			} else if (usedSpace + amount <= MAX_SPACE) {
				usedSpace = usedSpace + amount;
				System.out.println(Thread.currentThread().getName() + " remaining space : " + usedSpace);
			}
			return amount;
		}

		private boolean isTooLow() {
			return usedSpace >= MAX_THRESHOLD;
		}
	}
}

public class LivelockQueueWithConsumerMain {

	public static void main(String[] args) {

		Deque<Integer> queue = new LinkedList<>();

		int maxElements = 5;

		for (int i = 0; i < maxElements; i++) {
			queue.add(i);
		}

		Runnable consumer = () -> {
			Integer id = 0;
			int typeMessage = new Random().nextInt(maxElements);
			while ((id = queue.poll()) != null) {
				System.out.println("Processing value : " + id + " ...");
				if (id == typeMessage) {
					System.out
							.println("Simulate error for value : " + id + ". Rollback transaction and putting back to the queue");
					queue.addFirst(id);

				} else {
					System.out.println("Processing value : " + id + " complete");
				}
			}
		};

		ExecutorService executor = Executors.newCachedThreadPool();
		executor.execute(consumer);

		executor.shutdown();
	}
}