link to Github.

package rule66.compare.stop;

/**
 * 한 스레드의 boolean 값이 true가 되면 후면 스레드는 중지되는 프로그램이다.
 * 하지만 최소 약 10밀리세컨드 이상을 sleep하면 후면 스레드는 해당 루프를 돌면서 boolean 값은 자신이 바꿀 수 없는 값이고 항상 동일하다고 생각하여
 * 해당 변수를 무시하도록 소스를 변경한다. 이런 최적화를 JVM(Hotspot JVM)최적화 기술인 끌어올리기(hoisting)라고 하는데
 * 그 덕에 후면 스레드는 절대 끝나지 않는 경우가 발생한다.
 *
 * @author gwon
 * @history
 *          2019. 6. 24. initial creation
 */
public class FirstStopThread {
	private static boolean stopRequested = false;

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

		Thread bgThread = new Thread(new Runnable() {

			@Override
			public void run() {
				int i = 0;
				while (!stopRequested) {
//					System.out.println(i++); // 최적화가 발생하지 않는다. 해당 메서드는 Thread-safe하기 때문.
					i++;
				}

				/**
				 * hoisting할 경우 위 소스는 아래와 같이 변경된다.
				 * if (!stopRequested) {
				 * while (true) {
				 * i++;
				 * }
				 * }
				 *
				 *****************************/

				System.out.println("finished"); // 호출되지 않음

			}
		});

		bgThread.start();

//		Thread.sleep(1); // 아주 빠르게 true를 줄 경우, 최적화가 되기전에 true 되므로 후면 스레드는 종료된다.
		Thread.sleep(10);
		stopRequested = true;

	}
}
package rule66.compare.stop;

/**
 * {@link FirstStopThread} 수정하는 한 가지 방법은 boolean 값을 동기화하는 것이다.
 * synchronized 사용하여 동기화 할 경우, 당연하게 상호배제(mutually exclusive)를 제공한다.
 * 하지만 이 프로그램에서는 상호배제를 위한 동기화가 아니다. 동기화는 한 스레드가 만든 변화를 다른 스레드에서 관측할 수 있다.
 * 이 프로그램에서는 이를 위하여 동기화를 사용한다. 즉, 후면 스레드는 동기화 메서드인 stopRequested()의 변화를 지속적으로 관찰할 수 있도록 하여
 * 앞서 본 {@link FirstStopThread}의 문제점인 hoisting 최적화를 막아 정상적으로 프로그램이 종료되도록 한다.
 *
 * @author gwon
 * @history
 *          2019. 6. 24. initial creation
 */
public class SecondStopThreadSync {
	private static boolean stopRequested = false;

	private static void requestStop() {
		stopRequested = true;
	}

	private static synchronized boolean stopRequested() {
		return stopRequested;
	}

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

		Thread bgThread = new Thread(new Runnable() {

			@Override
			public void run() {
				int i = 0;
				while (!stopRequested()) {
					i++;
				}

				System.out.println("finished");

			}
		});

		bgThread.start();

		Thread.sleep(10);
		requestStop();

	}
}
package rule66.compare.stop;

/**
 * {@link SecondStopThreadSync}에서 설명했듯이, 이 프로그램에서 동기화는 상호배제를 목적으로 사용하지 않았다.
 * 즉, 락이 필요없다. 비록 순환문의 각 단계마다 동기화를 실행하는 비용이 크진 않지만 그래도 동기화를 쓰지 않는것보다는 비용이 크다.
 * 락이 필요없고 비용을 줄이고 싶다면 volatile를 사용하면 된다. volatile은 어떤 스래드건 가장 최근에 기록된 값을 읽도록 보장한다.
 * (하지만 volatile이 volatile을 사용하지 않는것보다 비용이 적은것은 아니다.)
 *
 * @author gwon
 * @history
 *          2019. 6. 24. initial creation
 */
public class ThirdStopThreadVolatile {
	private static volatile boolean stopRequested = false;

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

		Thread bgThread = new Thread(new Runnable() {

			@Override
			public void run() {
				int i = 0;
				while (!stopRequested) {
					i++;
				}

				System.out.println("finished");

			}
		});

		bgThread.start();

		Thread.sleep(10);
		stopRequested = true;

	}
}
package rule66.compare.increment;

/**
 * volatile은 데이터를 읽을때는 가장 최근에 기록된 값을 읽도록 보장하지만 쓰기에 대해서는 동기화를 지원하지 않는다.
 * getNum()으로 가장 최근에 기록된 값에서 ++을 하지만, 두 개의 쓰레드가 동시에 같은 값을 읽어서 ++할 경우에는 결국 같은 값을 쓰게 된다.
 * 따라서 아래 결과는 2000이 되지 않는다. 따라서 이럴 땐 상호배제를 위해 synchronized를 사용해야 한다. {@link SecondIncrementSync}
 *
 * @author gwon
 * @history
 *          2019. 6. 24. initial creation
 */
final class FirstIncrementVolatile {
	private static volatile int cnt = 0;

	public static int getNum() {
		return cnt++;
	}

	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					getNum();
				}

				System.out.println("t1 finished");
			}
		});

		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					getNum();
				}

				System.out.println("t2 finished");
			}
		});

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

		Thread.sleep(1000);

		System.out.println(cnt); // not 2000
	}
}
package rule66.compare.increment;

/**
 * 메서드를 동기화로 선언하면 여러 스레드가 동시에 호출하더라도 서로 겹쳐 실행되지 않는다.
 * 더 좋은 방법도 있다. 사실 아래와 같은 경우를 위해 자바에서는 atomic 패키지에 다양한 클래스를 제공한다. {@link ThirdAtomicLong}
 *
 * @author gwon
 * @history
 *          2019. 6. 24. initial creation
 */
final class SecondIncrementSync {
	private static int cnt = 0;

	// synchronized 메서드
	public static synchronized int getNum() {
		return cnt++;
	}

	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					getNum();
				}

				System.out.println("t1 finished");
			}
		});

		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					getNum();
				}

				System.out.println("t2 finished");
			}
		});

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

		Thread.sleep(1000);

		System.out.println(cnt); // 2000
	}
}
package rule66.compare.increment;

import java.util.concurrent.atomic.AtomicLong;

/**
 * {@link rule47} 어떤 라이브러리가 있는지 파악하고, 적절히 활용하라 규칙처럼 {@link SecondIncrementSync}과 같이
 * 원하는 일을 해주면서도, 동기화를 사용한 해법보다 성능도 좋은 클래스는 자바에서 제공한다.
 * 좀 더 자세한 설명은 {@link CAS} 참고
 *
 *
 * @author gwon
 * @history
 *          2019. 6. 24. initial creation
 */
final class ThirdAtomicLong {
	private static final AtomicLong cnt = new AtomicLong();

	public static long getNum() {
		return cnt.getAndIncrement();
	}

	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					getNum();
				}

				System.out.println("t1 finished");
			}
		});

		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					getNum();
				}

				System.out.println("t2 finished");
			}
		});

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

		Thread.sleep(1000);

		System.out.println(cnt); // 2000
	}
}
package rule66.compare.increment.cas;

/**
 * java.util.concurrent.atomic 패키지가 제공하는 클래스들은 원자적 연산을 수행할 수 있는 클래스들을 제공한다.
 * synchronized 키워드 없이도 여러 스레드들에 의해 병렬적으로 수행되어도 결과의 안전성을 보장한다.
 * 내부적으로는 CAS(Compare-And-Swap)을 활용하는데 이는 네이티브 메서드로 짜여 있으며 실제 메모리를 참조하여 구현되어 있다.
 * 아래 함수는 예시로 만든 CAS메서드와 CAS메서드를 이용하여 AtomicLong 클래스가 어떻게 원자적 연사를 수행하는지 보여준다.
 * (자바는 포인터가 없으므로 컴파일은 되지 않는다.)
 *
 * @author gwon
 * @history
 *          2019. 6. 25. initial creation
 */
public class CAS {

	/**
	 * 값을 변경하기전 자신이 기억하고 있는 값과 현재 모든 스레드가 공유하는 메모리 영역의 값을 비교한다.
	 * 비교하여 같지 않을 경우, 다른 스레드가 먼저 해당 값을 변경하였으므로 false를 리턴하여 호출한 곳에서 다시 새로운 값으로 호출하도록 한다.
	 * 비교하여 값이 같을 경우, 현재 스레드가 값을 변경할 차례로 판단하여 메모리 영역에 새로운 값을 저장하고 true를 리턴하여 호출한곳에서 끝내도록 한다.
	 *
	 * @param *pointer	모든 스레드가 공유하는 메모리 영역의 포인터.
	 * @param oldVal	값을 변경하기전 자신이 기억하고 있는 값
	 * @param newVal	변경하고자 하는 값
	 * @return
	 */
	public boolean compareAndSwap(int *pointer, int oldVal, int newVal) {
		if (*pointer != oldVal) {
			return false;
		}

		*pointer = newVal;
		return true;
	}

	/**
	 * 모든 스레드가 공유하는 메모리 영역의 값을 가지고 와, 현재 자신이 기억하는 값으로 저장한다.
	 * 1을 증가시키기 위해 CAS를 호출하는데 이 때 메모리 영역과, 현재 자신이 기억하는 값, 변경값을 인자로 주어 호출한다.
	 * 만약 이 때 동안 다른 스레드가 변경을 할 경우, 자신이 기억하고 있는 값과 메모리 영역의 값이 달라 CAS에서 false가 리턴되어
	 * 다시 현재 자신이 기억하는 값을 새롭게 업데이트하여 반복한다.
	 *
	 */
	public long getAndIncrement(){
		boolean done = false;
		int oldVal;
		while(!done){
			oldVal = *pointer;
			done = compareAndSwap(pointer, oldVal, oldVal + 1);
		}

		return oldVal + 1;
	}
}