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.......