http://www.iteye.com/wiki/jvm/2877-synchronized-volatile
一.java的内存模型JMM(解决可见性和有序性) 规定了jvm主内存
多线程不能相互传递数据,用共享变量
每个线程都有工作内存,其存储了主内存某些对象的副本.:
1)从主存复制对象到当前工作内存(read and load)
2)改变共享内存(use and assign) (其他线程可见)
3)工作内存数据刷新主存相关内容(store and write)
多次赋值时,可能只对工作内存副本操作,最后一次才同步到主存
导致不同步会出问题.
synchronized关键字保证了多个线程对于同步块是互斥的
而volatile关键字之解决多线程的内存可见性问题
二.java的线程同步机制
//不同步
package com.test;
public class Account {
private int balance;
public Account(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void add(int num) {
balance = balance + num;
}
public void withdraw(int num) {
balance = balance - num;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
Account account = new Account(1000);
Thread a = new Thread(new AddThread(account, 20), "add");//创建thread
Thread b = new Thread(new WithdrawThread(account, 20), "withdraw");
a.start();//启动
b.start();
a.join();
b.join();
System.out.println(i+":"+account.getBalance());
}
}
static class AddThread implements Runnable {
Account account;
int amount;
public AddThread(Account account, int amount) {
this.account = account;
this.amount = amount;
}
public void run() {
for (int i = 0; i < 1000000; i++) {
account.add(amount);
}
}
}
static class WithdrawThread implements Runnable {
Account account;
int amount;
public WithdrawThread(Account account, int amount) {
this.account = account;
this.amount = amount;
}
public void run() {
for (int i = 0; i < 1000000; i++) {
account.withdraw(amount);
}
}
}
}
public synchronized void add(int num) //锁方法所在对象
public static synchronized void add(int num) //锁方法所在的class
//在并发环境下,一个没有共享的对象作为锁是没有意义的
Object lock=new Object();
synchronized (lock){
//do something
}
锁有两个队列 :就绪(将获得锁),阻塞(被阻塞的进程)
wait(放弃锁)-->阻塞-->notify-->就绪-->cpu调度
临界区代码:
1)获得同步锁
2)清空工作内存
3)主存copy变量到工作内存
4)处理
5)工作内存-->主存
6)释放锁
//线程安全
public class Account {
List<Object> eggs = new ArrayList<Object>();
public synchronized Object getEgg() {
if (eggs.size() == 0) {
try {
wait(); // 放弃锁
} catch (InterruptedException e) {
}
}
Object egg = eggs.get(0);//
System.out.println("Get Egg,Eggs' size:" + eggs.size());
System.out.println("============================");
eggs.clear();// 清空盘子
notify(); // 唤醒
return egg;
}
public synchronized void putEgg(Object egg) {
if (eggs.size() > 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
eggs.add(egg);// 往盘子里放鸡蛋
System.out.println("Put Egg,Eggs' Size:" + eggs.size());
notify();// 唤醒阻塞队列的某线程到就绪队列
}
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
Thread put = new Thread(new Put(account));
Thread get = new Thread(new Get(account));
put.start();
get.start();
// put.join();
// get.join();
}
static class Put implements Runnable {
Account account;
Put(Account a) {
this.account = a;
}
public void run() {
for (int i = 0; i < 100000; i++) {
Object egg = new Object();
account.putEgg(egg);
}
}
}
static class Get implements Runnable {
Account account;
Get(Account a) {
this.account = a;
}
public void run() {
for (int i = 0; i < 1000000; i++) {
account.getEgg();
}
}
}
}
1 开始,A调用plate.putEgg方法,此时eggs.size()为0,因此顺利将鸡蛋放到盘子,还执行了notify()方法,唤醒锁的阻塞队列的线程,此时阻塞队列还没有线程。
2 又有一个A线程对象调用plate.putEgg方法,此时eggs.size()不为0,调用wait()方法,自己进入了锁对象的阻塞队列。
3 此时,来了一个B线程对象,调用plate.getEgg方法,eggs.size()不为0,顺利的拿到了一个鸡蛋,还执行了notify()方法,唤醒锁的阻塞队列的线程,此时阻塞队列有一个A线程对象,唤醒后,它进入到就绪队列,就绪队列也就它一个,因此马上得到锁,开始往盘子里放鸡蛋,此时盘子是空的,因此放鸡蛋成功。
4 假设接着来了线程A,就重复2;假设来料线程B,就重复3。
任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存
只保证可见性,不保证有序性
public class VolatileTest{
public volatile int a;
public void add(int count){
a=a+count;
}
}
//适合场景
满足条件
1)对变量的写操作不依赖于当前值。
2)该变量没有包含在具有其他变量的不变式中
用volatile声明了,读取主存副本到工作内存和同步a到主存的步骤,相当于是一个原子操作
public void setA(int a){
this.a=a;
}