多线程3:多线程同步

 多线程3:多线程同步_第1张图片

 

package com.test.thread;

public class ThreadTest {
	public static void main(String[] args) {
		Bank bank = new Bank();
		Thread t1 = new MoneyThread(bank);
		Thread t2 = new MoneyThread(bank);
		t1.start();
		t2.start();
	}
}

class Bank {
	int money = 1000;
	//取钱方法
	public int getMoney(int number) {
		if (number < 0) { //如果取的钱数少于0,则返回-1
			return -1;
		}
		if (number > money) {//如果取的钱数大于总数,则返回-2
			return -2;
		}
		try {
			Thread.sleep(1000); //睡一秒,模拟取款前的动作
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		money -= number;
		System.out.println("left money " + money);
		return number;
	}
}

class MoneyThread extends Thread {
	private Bank bank;
	public MoneyThread(Bank bank) {
		this.bank = bank;
	}

	public void run() {
		System.out.println(bank.getMoney(800));
	}
}

 

最后打印的结果为

left money -600
left money -600
800
800

 

 两个线程的run方法传入的都是800,至于最后为什么余额都是-600,是因为下面这行代码

money -= number;
System.out.println("left money " + money);

假设A线程执行money-=number后,此时money的值为200。B线程马上赶到,在A线程执行打印语句前执行

money -= number,此时money的值是-600,接下去两个打印语句都是打印-600。

 如果将getMoney方法改成

public synchronized int getMoney(int number) {

 加上synchronized 关键字后,打印结果为

left money 200
-2
800

 

这样就正确了。

synchronized 关键字:当synchronized 关键字修饰一个方法时,该方法就叫同步方法。

Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示该对象上锁。此时其它任何线程都无法访问该synchronized方法了,直到之前的那个线程执行方法完毕后,(或者是抛出异常),那么将该对象的锁释放掉,其它线程才有可能去访问该synchronized方法。

 

如果一个对象有多个synchronized方法,在某一时刻某个线程已进入到某个synchronized方法,那么在该方法执行完毕前,其它线程是无法访问该对象的任何synchronized方法的。

 

需额外注意的是:如果某个类中的某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在对象所对应的class对象,因为JAVA中无论一个类有多少个对象,这些对象会对应唯一一个class对象,因此当两个线程分别访问同一个类的两个对象的static synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程会先去执行方法,执行完成后,另一个线程才会开始。

 

synchronized块的写法:

synchronized(object){}

表示线程在执行时会对object对象上锁。

 

package com.test.thread;

public class ThreadTest2 {
	public static void main(String[] args) {
		Example example = new Example();
		ExampleThread1 thread1 = new ExampleThread1(example);
		ExampleThread2 thread2 = new ExampleThread2(example);
		thread1.start();
		thread2.start();
	}
}

class Example{
	public void execute(){
		synchronized (this) { //synchronized块
			for (int i = 0; i < 10; i++) {
				System.out.println("hello execute "+i);
			}
		}
	}
	public void execute2(){
		synchronized (this) { //synchronized块
			for (int i = 0; i < 10; i++) {
				System.out.println("hello execute2 "+i);
			}
		}
	} 
}

class ExampleThread1 extends Thread {
	private Example example;

	public ExampleThread1(Example example) {
		this.example = example;
	}

	public void run() {
		this.example.execute();
	}
}

class ExampleThread2 extends Thread {
	private Example example;

	public ExampleThread2(Example example) {
		this.example = example;
	}

	public void run() {
		this.example.execute2();
	}
}

打印结果为:

hello execute 0
hello execute 1
hello execute 2
hello execute 3
hello execute 4
hello execute 5
hello execute 6
hello execute 7
hello execute 8
hello execute 9
hello execute2 0
hello execute2 1
hello execute2 2
hello execute2 3
hello execute2 4
hello execute2 5
hello execute2 6
hello execute2 7
hello execute2 8
hello execute2 9

 

 

 通常建议使用synchronized块,更加细粒度。

synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法,synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内,synchronized块外的代码是可以被多个线程同时访问到的。

此外需注意的是被synchronized保护的变量应该是私有的。

 

 

 

多线程3:多线程同步_第2张图片

 

package com.test.thread;

/*
 * 此类实现在0,1之间切换
 */
public class Sample {
	private int number = 0;

	/**
	 * 当number值为0时加1,否则等待 
	 */
	public synchronized void increase() {
		while (0 != number) {
			try {
				wait();//等待,释放资源
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		number++;
		System.out.println(number);
		notify();  //唤醒对象
	}

	/**
	 * 当number值为1时减1,否则等待 
	 */
	public synchronized void decrease() {
		while (0 == number){
			try{
				wait();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		number --;
		System.out.println(number);
		notify();
	}
}

 

package com.test.thread;

public class IncreaseThread extends Thread {
	private Sample sample;

	public IncreaseThread(Sample sample) {
		this.sample = sample;
	}

	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep((long)(Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			sample.increase();
		}
	}
}

 

 

package com.test.thread;

public class DecreaseThread extends Thread{
	private Sample sample;

	public DecreaseThread(Sample sample) {
		this.sample = sample;
	}

	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep((long)(Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			sample.decrease();
		}
	}
}

 

package com.test.thread;

public class MainTest {
	public static void main(String[] args) {
		Sample sample = new Sample();
		IncreaseThread t1 = new IncreaseThread(sample);
		DecreaseThread t2 = new DecreaseThread(sample);
		IncreaseThread t3 = new IncreaseThread(sample);
		DecreaseThread t4 = new DecreaseThread(sample);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

 

上例有两个线程类:分别是IncreaseThread和DecreaseThread,在MainTest中生成了四个线程,用于同步访问Sample类,最后打印的结果为1,0.......

 

你可能感兴趣的:(线程同步)