JAVA线程安全

线程有可能只对工作内存中的副本进行赋值,只到最后一次赋值后才同步到主存储区,假设有一个共享变量x,线程a执行x=x+1。从上面的描述中可以知道x=x+1并不是一个原子操作,它的执行过程如下:

1 从主存中读取变量x副本到工作内存

2 x加1

3 将x加1后的值写回主存

如果另外一个线程b执行x=x-1,执行过程如下:

1 从主存中读取变量x副本到工作内存

2 给x减1

3 将x减1后的值写回主存 

那么显然,最终的x的值是不可靠的。假设x现在为10,线程a加1,线程b减1,从表面上看,似乎最终x还是为10,但是多线程情况下会有这种情况发生:

1:线程a从主存读取x副本到工作内存,工作内存中x值为10

2:线程b从主存读取x副本到工作内存,工作内存中x值为10

3:线程a将工作内存中x加1,工作内存中x值为11

4:线程a将x提交主存中,主存中x为11

5:线程b将工作内存中x值减1,工作内存中x值为9

6:线程b将x提交到中主存中,主存中x为9 

同样,x有可能为11,如果x是一个银行账户,线程a存款,线程b扣款,显然这样是有严重问题的,要解决这个问题,必须保证线程a和线程b是有序执行的,并且每个线程执行的加1或减1是一个原子操作


synchronized

public synchronized void add(int num)这种情况,锁就是这个方法所在的对象。同理,如果方法是public  static synchronized void add(int num),那么锁就是这个方法所在的class。


import java.util.ArrayList;

import java.util.List;

public class Plate {

Listeggs =new ArrayList();

    public synchronized ObjectgetEgg() {

while(eggs.size() ==0) {

try {

wait();

            }catch (InterruptedException e) {

}

}

Object egg =eggs.get(0);

        eggs.clear();// 清空盘子

        notify();// 唤醒阻塞队列的某线程到就绪队列

        System.out.println("拿到鸡蛋");

        return egg;

    }

public synchronized void putEgg(Object egg) {

while(eggs.size() >0) {

try {

wait();

            }catch (InterruptedException e) {

}

}

eggs.add(egg);// 往盘子里放鸡蛋

        notify();// 唤醒阻塞队列的某线程到就绪队列

        System.out.println("放入鸡蛋");

    }

static class AddThreadextends Thread{

private Plateplate;

        private Objectegg=new Object();

        public AddThread(Plate plate){

this.plate=plate;

        }

public void run(){

for(int i=0;i<5;i++){

plate.putEgg(egg);

            }

}

}

static class GetThreadextends Thread{

private Plateplate;

        public GetThread(Plate plate){

this.plate=plate;

        }

public void run(){

for(int i=0;i<5;i++){

plate.getEgg();

            }

}

}

public static void main(String args[]){

try {

Plate plate=new Plate();

            Thread add=new Thread(new AddThread(plate));

            Thread get=new Thread(new GetThread(plate));

            add.start();

            get.start();

            add.join();

            get.join();

        }catch (InterruptedException e) {

e.printStackTrace();

        }

System.out.println("测试结束");

    }

}



放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

测试结束


声明一个Plate对象为plate,被线程A和线程B共享,A放鸡蛋,B拿鸡蛋。假设

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。 

整个过程都保证了放鸡蛋,拿鸡蛋,放鸡蛋,拿鸡蛋。


你可能感兴趣的:(JAVA线程安全)