process : 집
Thread : 일을 하는 주체, 사람
모든 스레드는 스택에 공간을 가지게 된다.
스레드만드는 두가지 방법
1) thread클래스 상속 -> run 메소드 오버라이드
객체를 만들어 start메소드를 실행시키면 run메소드를 시작한다.
각각의 스레드가 독립적으로 실행된다.
2)Runnable 인터페이스를 상속받아 구현한다.
클래스가 어떤 클래스를 이미 상속받은 경우에 쓴다 (자바는 다중상속이 안되니까 )
그러나 스레드 클래스 객체의 힘을 빌려야한다. 혼자 실행할 수 없다.
jvm에 있는 스레드를 할당해주는 기능이 thread 클래스에 있기 때문에.
레지스터 - 책상, 메모리 -사물함
1) 싱글코어
스레드 a의 1,2,3의 과정이 끝날 때까지 제어권을 넘겨주면 안된다.
2)멀티코어 : cpu 여러개
결국 싱글코어든 멀티코어든 공유변수를 사용하면 문제가 발생할 수 있다.
(연산의 양이 많으면 많을수록, 스레드가 많으면 많을 수록 값이 왜곡될 가능성이 높아진다. )
--> 공유 변수를 사용하는 과정을 보호해야 한다. (ex.화장실)
= 동기화가 필요하다.
동기화
static 필드는 static synchronized로 동기화 처리
package ex06.thread.thread06;
class Anum{
static int num = 0;
// 이 메서드가 시작하고 끝날 때까지 다른 스레드의 진입을 허용하지 않겠다
// 즉 메서드 처리동안 1개 스레드만 연산가능하다
static synchronized void accumulate(int val) {
/*
우리 생각 : 눈으로 보는 코드는 1줄
실제 동작 : 1) num -> register
2) val -> register
3) cpu(ALU) : num+val -> register
4) register -> num
*/
num += val;
}
}
class Sum implements Runnable{
//static int num = 0; // static변수는 Class에 1개만 존재, 모든 객체가 공유
int start, end;
Sum(int start, int end){
this.start = start;
this.end = end;
}
@Override
public void run() {
// 아래의 num은 모든 객체가 공유하므로
// Context Switching시 변수의 값이 왜곡될 수 있다
// 그러므로 왜곡되지 않도록 하는 처리가 필요하다
// 이를 스레드 (값의) 동기화라고 한다
for(int i=start;i<=end;i++)
Anum.accumulate(i);
}
}
public class SyncThread {
public static void main(String[] args) throws InterruptedException {
Sum sum0 = new Sum(1, 50);
Sum sum1 = new Sum(51, 100);
Thread t0 = new Thread(sum0);
Thread t1 = new Thread(sum1);
t0.start();
t1.start();
//스레드는 독립적으로 처리되는 리소스이므로 main스레드와 자식스레드는 별개로 동작한다
//자식 스레드가 종료될 때까지 대기
t0.join(); //sum0의 run()이 리턴되면 join()을 리턴
t1.join(); //sum1의 run()이 리턴되면 join()을 리턴
System.out.println("1~100까지의 합 : " + Anum.num);
}
}
일반 필드
package ex06.thread.thread06;
class Anum{
int num = 0;
synchronized void accumulate(int val) {
num += val;
}
}
class Sum implements Runnable{
Anum aNum;
int start, end;
Sum(Anum aNum, int start, int end){
this.aNum = aNum;
this.start = start;
this.end = end;
}
@Override
public void run() {
for(int i=start;i<=end;i++)
aNum.accumulate(i);
}
}
public class SyncThread {
public static void main(String[] args) throws InterruptedException {
Anum aNum = new Anum(); //2개의 스레드가 공유하는 객체
Sum sum0 = new Sum(aNum, 1, 50);
Sum sum1 = new Sum(aNum, 51, 100);
Thread t0 = new Thread(sum0);
Thread t1 = new Thread(sum1);
t0.start();
t1.start();
t0.join();
t1.join();
System.out.println("1~100까지의 합 : " + aNum.num);
}
}
부분동기화
package ex06.thread.thread08;
class Anum{
int num = 0;
// 만약 메서드의 코드가 길다.
// 그런데 어떤 부분은 동기화가 필요하고,
// 어떤 부분은 필요없는 부분이 있을 수 있다.
// 그런데 메소드에 synchronized를 주면 전체를 동기화하므로 속도가 느려질 것이다. -->성능저하
// =>이럴 경우는 부분 동기화를 해주면 된다.
void accumulate(int val) {
// 모든 Class는 Object의 상속을 받는다.
// Object는 스레드에 대한 lock키가 내장되어 있다.
// 그러므로 1개의 동기화가 필요할 때는 자신의 객체를 자물쇠로 사용하면 된다.
// -> 연산의 문제가 생길 부분만 이렇게 동기화시키면 된다.
synchronized(this) {
num += val;
}
}
}
class Sum implements Runnable{
Anum aNum;
int start, end;
Sum(Anum aNum, int start, int end){
this.aNum = aNum;
this.start = start;
this.end = end;
}
@Override
public void run() {
for(int i=start;i<=end;i++)
aNum.accumulate(i);
}
}
public class SyncThread {
public static void main(String[] args) throws InterruptedException {
Anum aNum = new Anum(); //2개의 스레드가 공유하는 객체
Sum sum0 = new Sum(aNum, 1, 50);
Sum sum1 = new Sum(aNum, 51, 100);
Thread t0 = new Thread(sum0);
Thread t1 = new Thread(sum1);
t0.start();
t1.start();
t0.join();
t1.join();
System.out.println("1~100까지의 합 : " + aNum.num);
}
}
누적차 메서드도 생성
package ex06.thread.thread09;
class Anum {
int accuNum = 0;
int diffNum = 0;
void accumulate(int val) {
synchronized (this) {
accuNum += val;
}
}
void calcDiff(int val) {
diffNum -= val;
}
}
class Sum implements Runnable {
Anum aNum;
int start, end;
Sum(Anum aNum, int start, int end) {
this.aNum = aNum;
this.start = start;
this.end = end;
}
@Override
public void run() {
for (int i = start; i <= end; i++)
aNum.accumulate(i);
}
}
class Minus implements Runnable {
Anum aNum;
int start, end;
Minus(Anum aNum, int start, int end) {
this.aNum = aNum;
this.start = start;
this.end = end;
}
@Override
public void run() {
for (int i = start; i <= end; i++)
aNum.calcDiff(i);
}
}
public class SyncThread {
public static void main(String[] args) throws InterruptedException {
Anum aNum = new Anum();
//누적 합을 구하기 위한 스레드
Sum sum0 = new Sum(aNum, 1, 50);
Sum sum1 = new Sum(aNum, 51, 100);
Thread t0 = new Thread(sum0);
Thread t1 = new Thread(sum1);
//누적 차를 구하기 위한 스레드
Minus minus0 = new Minus(aNum, 1, 50);
Minus minus1 = new Minus(aNum, 51, 100);
Thread t2 = new Thread(minus0);
Thread t3 = new Thread(minus1);
t0.start();
t1.start();
t2.start();
t3.start();
t0.join();
t1.join();
t2.join();
t3.join();
System.out.println("1~100까지의 누적합 : " + aNum.accuNum);
System.out.println("1~100까지의 누적차 : " + aNum.diffNum);
}
}