一、基本概念
异步编程模式与同步编程模式
异步编程模式:t1线程执行t1的,t2线程执行t2的.两个线程谁也不等谁。
同步编程模式:这里的同指的是协同。t1和t2线程执行的时候,t1必须等t2线程执行完毕之后才能执行。同步机制使程序等同单线程。
二、java多线程同步的目的
为了数据的安全,暂时放弃执行的效率
三、线程同步的条件
1.多线程环境
2.共享同一资源
3.数据涉及到修改操作
四、下面例子演示了在线程不同步时会出现的问题
package com.gdzy.SynchronizedTest;
/**
* 此程序模拟一个银行账户取款的操作, 有多个取款线程同时对一个账户进行操作, 在不使用线程同步时会出现错误
*/
public class SynchronizedTest01 {
public static void main(String[] args) { //主程序
Account acc = new Account("张三",10000);
Withdrawal w1 = new Withdrawal(acc);
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
Thread t4 = new Thread(w1);
Thread t5 = new Thread(w1);
Thread t6 = new Thread(w1);
t1.setName("取款人--1");
t2.setName("取款人--2");
t3.setName("取款人--3");
t4.setName("取款人--4");
t5.setName("取款人--5");
t6.setName("取款人--6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
//定义一个账户类
class Account{
private String accName; //定义一个账户名
private double balance; //定义账户余额
//无参构造
public Account(){
}
//有参构造
public Account(String accName,double balance){
this.accName = accName;
this.balance = balance;
}
//get set方法
public String getAccName(){
return this.accName;
}
public void setAccName(String accName){
this.accName = accName;
}
public double getBalance(){
return this.balance;
}
public void setBalance(double balance){
this.balance = balance;
}
//提供一个更新账户取款后余额的方法
public void updateAcc(double money){
double remain = balance - money;
this.balance = remain;
}
}
//定义一个取款操作的线程
class Withdrawal implements Runnable{
// 将取款类 与 账户类关联起来
Account acc;
//无参构造
public Withdrawal(){
}
//有参构造
public Withdrawal(Account acc){
this.acc = acc;
}
//重写run方法, 完成一次取款1000后更新并输出账户余额
public void run() {
acc.updateAcc(1000);
System.out.println(Thread.currentThread()+"---取款成功,帐户余额为: "+acc.getBalance());
}
}
代码运行后, 会发现由于多个线程对同一账户同时进行取款操作, 导致余额刷新出现的混乱
Thread[取款人--3,5,main]---取款成功,帐户余额为: 7000.0
Thread[取款人--4,5,main]---取款成功,帐户余额为: 6000.0
Thread[取款人--2,5,main]---取款成功,帐户余额为: 7000.0
Thread[取款人--6,5,main]---取款成功,帐户余额为: 4000.0
Thread[取款人--5,5,main]---取款成功,帐户余额为: 5000.0
Thread[取款人--1,5,main]---取款成功,帐户余额为: 7000.0
在java中多线程中,由于同时对同一资源同时进行操作而引起的错误,为了解决这个问题,引入synchronized关键字, 即在线程代码中, 对操作同一资源的方法或对方法中操作同一资源的语句加锁(synchronized)。
如下代码可解决实际的问题:
package com.gdzy.SynchronizedTest;
/**
* 此程序模拟一个银行账户取款的操作, 有多个取款线程同时对一个账户进行操作, 在不使用线程同步时会出现错误
*/
public class SynchronizedTest02 {
public static void main(String[] args) { //主程序
Account02 acc = new Account02("张三",10000);
Withdrawal02 w1 = new Withdrawal02(acc);
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
Thread t4 = new Thread(w1);
Thread t5 = new Thread(w1);
Thread t6 = new Thread(w1);
t1.setName("取款人--1");
t2.setName("取款人--2");
t3.setName("取款人--3");
t4.setName("取款人--4");
t5.setName("取款人--5");
t6.setName("取款人--6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
//定义一个账户类
class Account02{
private String accName; //定义一个账户名
private double balance; //定义账户余额
//无参构造
public Account02(){
}
//有参构造
public Account02(String accName,double balance){
this.accName = accName;
this.balance = balance;
}
//get set方法
public String getAccName(){
return this.accName;
}
public void setAccName(String accName){
this.accName = accName;
}
public double getBalance(){
return this.balance;
}
public void setBalance(double balance){
this.balance = balance;
}
//提供一个更新账户取款后余额更新的方法
public void updateAcc(double money){
//同步之后还需要加上Thread.sleep方法, 使之具有运行的时间????
double remain = balance - money;
this.balance = remain;
//setBalance(remain); //也可以通过set方法来设置余额
// 第二种同步(锁) 第一种相当于锁大门, 第二种相当于锁房间
}
}
//定义一个取款操作的线程
class Withdrawal02 implements Runnable{
// 将取款类 与 账户类关联起来
Account02 acc;
//无参构造
public Withdrawal02(){
}
//有参构造
public Withdrawal02(Account02 acc){
this.acc = acc;
}
//重写run方法, 完成一次取款1000后更新并输出账户余额
public synchronized void run(){ //再需要同步(加锁)的方法前加上synchronized关键字, 也可以在内部写一个同步的代码块
acc.updateAcc(1000);
System.out.println(Thread.currentThread()+":取款成功,帐户余额为: "+acc.getBalance());
// 也可通过下面加synchronized代码块的方式, 将对同一资源操作的代码进行加锁限定
// synchronized(this){
//
// acc.updateAcc(1000);
//
//
// System.out.println(Thread.currentThread()+":取款成功,帐户余额为: "+acc.getBalance());
// }
}
}
运行后得到的结果如下:
Thread[取款人--1,5,main]:取款成功,帐户余额为: 9000.0
Thread[取款人--6,5,main]:取款成功,帐户余额为: 8000.0
Thread[取款人--5,5,main]:取款成功,帐户余额为: 7000.0
Thread[取款人--4,5,main]:取款成功,帐户余额为: 6000.0
Thread[取款人--3,5,main]:取款成功,帐户余额为: 5000.0
Thread[取款人--2,5,main]:取款成功,帐户余额为: 4000.0