12. 쓰레드의 동기화
- 멀티쓰레드의 경우 여러 쓰레드가 같은 프로세스 내의 자원 공유
- 쓰레드A가 작업하던 도중 쓰레드B에게 제어권이 넘어가 공유데이터를 변경하면 의도와 다른 결과가 나온다
package com.thread.day1;
public class ThreadTest5 {
public static void main(String[] args) {
MyRunnable4 r = new MyRunnable4();
Thread th1 = new Thread(r);
Thread th2 = new Thread(r);
th1.start();
th2.start();
}
}
class MyRunnable4 implements Runnable{
int insVar = 0; //공유
@Override
public void run() {
int localVar = 0;
String name = Thread.currentThread().getName();
while(localVar<3) {
System.out.println(name + " local var : "+ ++localVar);
System.out.println(name+"--------------instance var : "+ ++insVar);
System.out.println();
}
}
}
/*
Thread-1 local var : 1
Thread-1--------------instance var : 1
Thread-0 local var : 1
Thread-1 local var : 2
Thread-1--------------instance var : 3
Thread-1 local var : 3
Thread-1--------------instance var : 4
Thread-0--------------instance var : 2
Thread-0 local var : 2
Thread-0--------------instance var : 5
Thread-0 local var : 3
Thread-0--------------instance var : 6
*/
package com.thread.day1;
class Circle{
int insR=0;
}
class MyThread5 extends Thread{
Circle c;
MyThread5(Circle c){
this.c=c;
}
public void run() {
int localVar=0;
while(localVar<3) {
System.out.println(getName()+" localVar : "+ ++localVar);
System.out.println(getName()+" --------insVar : "+ ++c.insR+"\n");
}
}
}
public class ThreadTest6 {
public static void main(String[] args) {
Circle c = new Circle();
MyThread5 th = new MyThread5(c);
MyThread5 th2 = new MyThread5(c);
th.start();
th2.start();
}
}
/*
* Thread-0 localVar : 1
Thread-1 localVar : 1
Thread-1 --------insVar : 2
Thread-1 localVar : 2
Thread-0 --------insVar : 1
Thread-0 localVar : 2
Thread-1 --------insVar : 3
Thread-1 localVar : 3
Thread-1 --------insVar : 5
Thread-0 --------insVar : 4
Thread-0 localVar : 3
Thread-0 --------insVar : 6
*/
- 다른 객체를 생성했지만, 같은 멤버변수를 공유
1. 동기화 (Synchronized)
- 한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 락을 걸어 데이터의 일관성 유지
- 객체에 락을 걸 때
synchronized(객체의 참조변수){}
- 메서드에 락을 걸 때
public synchronized void func1(){}
- 한쪽에서 쓰는 동안 다른 쪽을 대기시킨다
- 공유자원을 액세스하는 문장을 synchronized로 감싸면 동시에 두 쓰레드가 하나의 객체를 액세스하지 않는다
- 먼저 작업중이던 쓰레드가 작업을 마치기 전까지는, 다른 쓰레드에게 제어권이 넘어가더라도 데이터가 변경되지 않도록 보호
synchronized()로 감싸기
package com.thread.day1;
class Account{
int balance = 1000;
public void withdraw(int money) {
synchronized(this) {
if(balance >= money) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
balance -= money;
}
}
}
}
class MyRunnable6 implements Runnable{
Account acc = new Account();
public void run() {
while(acc.balance>0) {
int money=(int)(Math.random()*3+1)*100;
acc.withdraw(money);
System.out.println(Thread.currentThread().getName()+":balance="+acc.balance+", money="+money);
}
}
}
public class SyncTest {
public static void main(String[] args) {
MyRunnable6 r = new MyRunnable6();
Thread th = new Thread(r);
Thread th2 = new Thread(r);
th.start();
th2.start();
}
}
메서드에 락 걸기
package com.thread.day1;
class Account{
int balance = 1000;
public synchronized void withdraw(int money) {
if(balance >= money) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
balance -= money;
}
}
}
class MyRunnable6 implements Runnable{
Account acc = new Account();
public void run() {
while(acc.balance>0) {
int money=(int)(Math.random()*3+1)*100;
acc.withdraw(money);
System.out.println(Thread.currentThread().getName()+":balance="+acc.balance+", money="+money);
}
}
}
public class SyncTest {
public static void main(String[] args) {
MyRunnable6 r = new MyRunnable6();
Thread th = new Thread(r);
Thread th2 = new Thread(r);
th.start();
th2.start();
}
}
진행바
package com.thread.day1;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
public class ProgressBarTest extends JFrame implements ActionListener, Runnable {
JButton bt;
JProgressBar bar;
JTextField tfMemo;
JLabel lb;
Thread th;
public ProgressBarTest() {
this.setLayout(new FlowLayout());
lb = new JLabel("메모");
tfMemo = new JTextField(30);
add(lb);
add(tfMemo);
add(bt = new JButton("시작"));
bar = new JProgressBar();
bar.setStringPainted(true); // 진행바에 퍼센티지 표시
add(bar);
bt.addActionListener(this);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400,300);
setVisible(true);
}
public static void main(String[] args) {
new ProgressBarTest();
}
@Override
public void run() {
synchronized(bar) {
System.out.println(bar.getMinimum()+ " " +bar.getMaximum());
for (int i = (bar.getMinimum());i<=bar.getMaximum(); i+=5) {
System.out.println("i="+i);
try {
Thread.sleep(200);
}catch (InterruptedException e) {
e.printStackTrace();
}
bar.setValue(i);
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
th = new Thread(this);
th.start(); //runnable로 진입시킨다
}
}