一 并发的需求
在java的开发中,经常会有多线程的使用,在多线程使用时,就不可避免要访问同一个变量,这时就需要对共享变量进行控制,防止对一个变量同时访问时出错,在java中有synchronized关键字及其他方法进行并行控制,这里对synchronized进行一个介绍,首先引入一个例子,说明并发控制的必要性。
例:假设一家公司有n个部门,每个部门都要给一个人发工资,用一个int变量money代表一个人的工资,现在每个部门用一个线程给这个人加工资,如果不加控制,我们对这个人发工资时,用如下代码,Person代表人,add()方法对该人进行加工资,另外用一个AddTask类表示多线程:
public class Person {
static int money = 0;
public void add(){
money += 10;
money -= 10;
money += 10;
}
public static void main(String []args){
Person person = new Person();
Thread []myTask = new Thread[100];
AddTask addTask = new AddTask(person);
// 100个线程,每个线程都给加工资100;
for (int i = 0 ; i< 100 ;i ++){
myTask[i] = new Thread(addTask);
myTask[i].start();
}
// 等待所有线程执行完毕,如果所有线程不join(),会有可能出现主线程比其他线程先执行完毕,导致最后的结果仍然不正确
for (int i = 0 ; i< 100 ;i ++){
try {
myTask[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("The money is "+money);
}
}
public class AddTask implements Runnable {
Person person;
public AddTask(Person person){
this.person = person;
}
@Override
public void run() {
// TODO Auto-generated method stub
person.add();
}
}
结果如下:
The money is 990
工资发生了错误,当然这是偶然情况,但是偶然的发生就说明了问题的所在。
因为多个线程同时访问工资变量时,出现的问题。
二 Java内部锁(Intrinsic Locks或Monitor Lock)
因为java synchronized 用到了java 内部锁,先对内部锁进行简单介绍。Java的Object,类中就有Intrinsic lock,和wait(),notify()一样,因此所有对象都有这个锁。在java中可以对任意一个对象请求他的内部锁,如果获取成功,则程序正常执行;如果获取失败,(即已经被其他线程申请走了)就阻塞,直至其他线程使用释放该锁。
Java也有Lock类,这里不做介绍。
三 Synchronized 介绍
对于需要同步变量如何解决呢?用synchronized关键字,synchronized关键字主要有两种使用方法:
1 synchronized 修饰方法
当一个线程调用synchronized方法时,自动获取方法所在对象(!!)的锁,方法返回时释放锁。对于static方法,也是一样的,只不过由于static属于类所有,java的类对应一个Class object,调用static synchronized方法时,获取Class object的锁,返回时释放。
如下对add()方法加上synchronized关键字时,其他线程访问add()时,需要先获取person对象的锁。所有的线程在初始化时传入同一个person对象,将来访问add()时,都需要先获取该person对象的锁,防止同步时出错。
public class Person {
static int money = 0;
public synchronized void add(){
money += 10;
money -= 10;
money += 10;
}
public static void main(String []args){
Person person = new Person();
Thread []myTask = new Thread[100];
AddTask addTask = new AddTask(person);
// 100个线程,每个线程都给加工资100;
for (int i = 0 ; i< 100 ;i ++){
myTask[i] = new Thread(addTask);
myTask[i].start();
}
// 等待所有线程执行完毕
for (int i = 0 ; i< 100 ;i ++){
try {
myTask[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("The money is "+money);
}
}
2 synchronized 语句,在oracle中叫synchronized statement
这种情况synchronized必须指明提供锁的对象,比如this,指的是当前所在的对象,也可以是其他对象。而且需要注意的时,所有线程必须用synchronized指定的是同一个对象。如下代码也可以解决同步的问题。
public class Person {
static int money = 0;
public void add(){
synchronized(this){
money += 10;
money -= 10;
money += 10;
}
}
public static void main(String []args){
Person person = new Person();
Thread []myTask = new Thread[100];
AddTask addTask = new AddTask(person);
// 100个线程,每个线程都给加工资100;
for (int i = 0 ; i< 100 ;i ++){
myTask[i] = new Thread(addTask);
myTask[i].start();
}
// 等待所有线程执行完毕
for (int i = 0 ; i< 100 ;i ++){
try {
myTask[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("The money is "+money);
}
}