首先小结一下释放锁的情况:
当前线程的同步方法、同步代码块执行结束。
当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行。
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。(应尽量避免使用suspend()和resume()来控制线程)
小练习
银行有一个账户。有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
问题:该程序是否有安全问题,如果有,如何解决?
【提示】
两个储户,涉及多线程。
同一个账户,共享数据。
(多线程不一定涉及线程安全,只有操作共享数据时才涉及线程安全。)
明确多线程运行代码中哪些语句是操作共享数据的。
第一印象,未考虑同步机制,出现线程安全问题,如下:
package com.ucar.quan.thread;
public class AccountTest {
/**
* @param args
* 权兴权意-20160811
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Account account = new Account();
Customer c1 = new Customer(account);
Customer c2 = new Customer(account);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
class Account{
double balance;//余额
public Account(){
}
public void deposit(double amt){
balance += amt;
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + balance);
}
}
class Customer extends Thread{
Account account;
public Customer(Account account){
this.account = account;
}
public void run(){
for(int i = 0;i < 3;i++){
account.deposit(1000);
}
}
}
注释掉休眠代码,减小问题发生几率,如下:
package com.ucar.quan.thread;
public class AccountTest {
/**
* @param args
* 权兴权意-20160811
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Account account = new Account();
Customer c1 = new Customer(account);
Customer c2 = new Customer(account);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
class Account{
double balance;//余额
public Account(){
}
public void deposit(double amt){
balance += amt;
// try {
// Thread.currentThread().sleep(10);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + ":" + balance);
}
}
class Customer extends Thread{
Account account;
public Customer(Account account){
this.account = account;
}
public void run(){
for(int i = 0;i < 3;i++){
account.deposit(1000);
}
}
}
使用同步方法,如下:
package com.ucar.quan.thread;
public class AccountTest {
/**
* @param args
* 权兴权意-20160811
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Account account = new Account();
Customer c1 = new Customer(account);
Customer c2 = new Customer(account);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
class Account{
double balance;//余额
public Account(){
}
//synchronized 当前对象锁,确保唯一。
public synchronized void deposit(double amt){
balance += amt;
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + balance);
}
}
class Customer extends Thread{
Account account;
public Customer(Account account){
this.account = account;
}
public void run(){
for(int i = 0;i < 3;i++){
account.deposit(1000);
}
}
}