线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。例如一个银行账户同时被两个线程操作,一个取100块,一个存钱100块。如果取钱线程和存钱线程同时发生,就会出现异常。因此多线程同步就是要解决这个问题,如下面的代码
package com.example;
/**
* Created by Owen Chan on 16/3/14.
* Copyright © 2016 Owen Chan. All rights reserved.
*/
public class BankAccount {
private int amount = 0;
//deposit money
public void depositMoney(int money){
amount += money;
System.out.println(System.currentTimeMillis() + "deposit money : " + money);
}
//withdraw money
public void withdrawMoney(int money){
if(amount - money < 0){
System.out.println("account has no enough money");
return;
}
amount -= money;
System.out.println(System.currentTimeMillis() +"withdraw money : " + money);
}
//enquiries
public void enquiriesAccount(){
System.out.println("account last: "+amount);
}
}
package com.example;
public class MyClass {
public static void main(String args[]) {
final BankAccount bank = new BankAccount();
Thread tadd = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bank.depositMoney(10);
bank.enquiriesAccount();
System.out.println("\n");
}
}
});
Thread tsub = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
bank.withdrawMoney(10);
bank.enquiriesAccount();
System.out.println("\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
tsub.start();
tadd.start();
}
}
输出结果:
account has no enough money
account last: 0
1457947900621deposit money : 10
1457947900621withdraw money : 10
account last: 0
account last: 0
从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问BankAccount对象并修改其数据所致。
同步和锁定
Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例有关的锁。如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放锁。
同步后的代码
package com.example;
/**
* Created by Owen Chan on 16/3/14.
* Copyright © 2016 Owen Chan. All rights reserved.
*/
public class BankAccount {
private int amount = 0; //账户余额
//deposit money
public synchronized void depositMoney(int money){
amount += money;
System.out.println(System.currentTimeMillis() + "deposit money : " + money);
}
//withdraw money
public synchronized void withdrawMoney(int money){
if(amount - money < 0){
System.out.println("account has no enough money");
return;
}
amount -= money;
System.out.println(System.currentTimeMillis() +"withdraw money : " + money);
}
//enquiries
public void enquiriesAccount(){
System.out.println("account last: "+amount);
}
}
输出结果如下:
account has no enough money
account last: 0
1457949577270deposit money : 10
account last: 10
1457949577270withdraw money : 10
account last: 0
静态方法同步
要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。
例如:
public static synchronized int setAccount(String name){
Xxx.name = name;
}
等价于
public static int setAccount(String name){
synchronized(Xxx.class){
Xxx.name = name;
}
}
线程死锁
当两个线程被阻塞,每个线程在等待另一个线程时就发生死锁。
public class DeadlockRisk {
private static class Resource {
public int value;
}
private Resource resourceA = new Resource();
private Resource resourceB = new Resource();
public int read() {
synchronized (resourceA) {
synchronized (resourceB) {
return resourceB.value + resourceA.value;
}
}
}
public void write(int a, int b) {
synchronized (resourceB) {
synchronized (resourceA) {
resourceA.value = a;
resourceB.value = b;
}
}
}
}
假设read()方法由一个线程启动,write()方法由另外一个线程启动。读线程将拥有resourceA锁,写线程将拥有resourceB锁,两者都坚持等待的话就出现死锁。
线程同步总结
1、线程同步的目的是防止多个线程访问一个数据对象时,对数据的破坏
2、线程同步方法是可以通过锁来实现,每个对象都有切仅有一个锁,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
5、死锁是线程间相互等待锁锁造成的。