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保护的变量应该是私有的。
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.......