1.스레드
2.스레드 동기화
3.예외처리
4.JVM
main메서드이며, 메인 스레드가 main 메서드를 실행시켜준다.

run() 메서드 내에 스레드가 처리할 작업을 작성run()메서드는 Runnable 인터페이스와 Thread 클래스에 정의되어져 있다.Runnable 인터페이스를 구현한 객체에서 run()을 구현하여 스레드를 생성하고 실행하는 방법
public class ThreadExample1 {
    public static void main(String[] args) {
        // Runnable 인터페이스를 구현한 객체 생성
        Runnable task1 = new ThreadTask1();
        // Runnable 구현 객체를 인자로 전달하면서 Thread 클래스를 인스턴스화하여 스레드를 생성
        Thread thread1 = new Thread(task1);
        // 위의 두 줄을 아래와 같이 한 줄로 축약 가능
        // Thread thread1 = new Thread(new ThreadTask1());
				// start() 메서드를 통해 run()메서드 내부의 코드 실행
				thread1.start();
//반복문 추가
				for (int i = 0; i < 100; i++) {
            System.out.print("@");
        }
    }
}
// Runnable 인터페이스를 구현하는 클래스
class ThreadTask1 implements Runnable {
    // run() 메서드 바디에 스레드가 수행할 작업 내용 작성
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.print("#");
        }
    }
}
// 익명 Runnable 구현 객체를 활용하여 스레드 생성
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.print("#");
                }
            }
        });
        thread1.start();
        for (int i = 0; i < 100; i++) {
            System.out.print("@");
        }
    }
}
Thread 클래스를 상속 받은 하위 클래스에서 run()을 구현하여 스레드를 생성하고 실행하는 방법
public class ThreadExample2 {
    public static void main(String[] args) {
// Thread 클래스를 상속받은 클래스를 인스턴스화하여 스레드를 생성
        Thread thread2 = new ThreadTask2();
// 작업 스레드를 실행시켜, run() 내부의 코드를 처리
thread2.start();
        // 반복문 추가
        for (int i = 0; i < 100; i++) {
            System.out.print("@");
}
    }
}
// Thread 클래스를 상속받는 클래스 작성
class ThreadTask2 extends Thread {
// run() 메서드 바디에 스레드가 수행할 작업 내용 작성
    public void run() {
				for (int i = 0; i < 100; i++) {
            System.out.print("#");
    }
}스레드의_참조값.getName()으로 조회스레드의_참조값.setName()으로 설정currentThread()Thread.currentThread().getName()Thread.sleep(1000);try { … } catch ( ~ ) { … }try의 블록 내의 코드를 실행하다가 예외 또는 에러가 발생하면 catch문의 블럭에 해당하는 내용을 실행synchronized 키워드 사용// 1. 메서드 전체를 임계 영역으로 지정
// 반환 타입 좌측에 synchronized 키워드를 작성
public synchronized boolean withdraw(int money) {
	    if (balance >= money) {
	        try { Thread.sleep(1000); } catch (Exception error) {}
	        balance -= money;
// 2. 특정한 영역을 임계 영역으로 지정
// synchronized 키워드와 함께 소괄호(()) 안에 해당 영역이 포함된 객체의 참조를 넣고, 중괄호({})로 블럭을 열어, 블럭 내에 코드를 작성
public boolean withdraw(int money) {
			synchronized (this) {
			    if (balance >= money) {
			        try { Thread.sleep(1000); } catch (Exception error) {}
			        balance -= money;start()는 스레드를 실행시키는 메서드는 아니라, 실행대기 상태로 만들어주는 메서드다.실행 상태에서 일시 정지(TIMED_WAITING) 상태로 전환static void sleep(long milliSecond)interrupt()를 호출한 경우interrupt()가 호출되면 기본적으로 예외가 발생하기 때문에 try … catchsleep(), wait(), join()에 의해 일시 정지 상태에 있는 스레드들을 실행 대기멈춰 있는 스레드.interrupt()를 호출하면, 기존에 호출되어 스레드를 멈추게 했던 sleep(), wait(), join()메서드에서 예외가 발생되며, 그에 따라 일시 정지가 풀리게 된다.yield()를 호출하면 남은 실행 시간 2초는 다음 스레드에게 양보static void yield()일시 중지상태로 만드는 상태 제어 메서드interrupt()가 호출되거나, join()호출 시 지정했던 다른 스레드가 모든 작업을 마치면 다시 실행 대기상태로 복귀void join()void join(long milliSecond)sleep()은 Thread 클래스의 static메서드 Thread.sleep(1000);join()은 특정 스레드에 대해 동작하는 인스턴스 메서드 thread1.join();스레드A가 공유 객체에 자신의 작업을 완료 → 스레드B와 교대하기 위해 notify()를 호출 notify()가 호출되면 스레드B가 실행 대기상태가 되며, 곧 실행
   스레드A는 wait()을 호출하며 자기 자신을 일시 정지상태로 만든다.
 WorkObject sharedObject = new WorkObject();
        ThreadA threadA = new ThreadA(sharedObject);
        ThreadB threadB = new ThreadB(sharedObject);
        threadA.start();
        threadB.start();
    }
}
class WorkObject {
    public synchronized void methodA() {
        System.out.println("ThreadA의 methodA Working");
        notify();
        try { wait(); } catch(Exception e) {}
    }
    public synchronized void methodB() {
        System.out.println("ThreadB의 methodB Working");
        notify();
        try { wait(); } catch(Exception e) {}
    }
}
class ThreadA extends Thread {
    private WorkObject workObject;
    public ThreadA(WorkObject workObject) {
        this.workObject = workObject;
    }
    public void run() {
        for(int i = 0; i < 10; i++) {
            workObject.methodA();
        }
    }
}
class ThreadB extends Thread {
    private WorkObject workObject;
    public ThreadB(WorkObject workObject) {
        this.workObject = workObject;
    }
    public void run() {
        for(int i = 0; i < 10; i++) {
            workObject.methodB();
        }
    }