多线程安全修改全局变量
2017-12-09 17:06
363 查看
一步一步分析问题先看下面1.当我开1000个线程出售1000张票时,数据正常,但是当我开1100个时就会出现负数?为什么啊?明明已经加了判断count>0
public class C{ int count=1000;//火车票 static long currenttime; static long endtime; static File file=new File("C:/Users/绝影/Desktop/log.txt"); static OutputStream out=null; static OutputStreamWriter output=null; static { System.out.println("我是静态块"); try { out=new FileOutputStream(file); output=new OutputStreamWriter(out); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void one() throws IOException { // TODO 线程输出方法 try { if(count>0) { synchronized(this) { output.write("车票"+(count--)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n"); System.out.println("车票"+(count)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n"); } }else{ endtime=System.currentTimeMillis(); System.out.println(endtime); System.out.println("抱歉现在没有车票了"+"总共用了时间"+(endtime-currenttime)+"卖完完余票"); System.exit(0); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { output.flush(); } } public static void main(String[] args) throws InterruptedException { currenttime=System.currentTimeMillis(); final C output = new C(); final R b = new R(); final BB e = new BB(); System.out.println("当前时间"+currenttime); //方式1 for(int j=1;j<=1100;j++) { new Thread() { public void run() { try { try { sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }//模仿网络延迟 output.one(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }; }.start(); } } }2.分析如下if(count>0) { synchronized(this) { output.write("车票"+(count--)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n"); System.out.println("车票"+(count)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n"); } }分析应该是这样的,虽然加了synchronized保证了同一个时刻当出现多个线程抢占时,这时候只有一个线程能进入到synchronized块中,但是对于if(count>0) 判断其它线程还是可以访问。因为我电脑时双核四线程,双核四线程是每个核心不停地在两个线程之间切换,以达到和四核相似的处理能力,自然同一个时刻可以跑两个线程。如果我们假设某一时刻内存中的数count是1,执行类似i--操作,比方第1000个线程进行了if(count>0)后运行同步块中的内容,这时候第1001个线程已经拷贝了之前内存中变量1的副本进行了if(count>0)操作,遇到了 synchronized发现有线程在使用,此时等待,当第1000个线程执行完同步副本变量到内存时,此时count=0,然后第1001个线程已经用之前的变量副本1进行了count>0的判断,所以还是进入了同步块,自然就出现了负数问题。为了验证假设当我在同步快中再次执行if(count>0)操作后在操作变量后,count不在出现负数,说明假设成立,也说明我们线程在使用到变量时,每次都会去读取当前内存中的当前变量内容。多线程不同步出现线程共享变量安全是因为每次读取的内存中内容不是最新更新的一个才导致。其实单核多线程我觉得也会出现,当第1000个执行了if(count>0)的判断后让出了cpu,切换到了第1001个执行,第1001个执行完判断并且执行完同步块中的内容更新内存中的count为0,但是第1000个线程已经执行过if(count>0),所以不会再执行这个判断,会接着往后执行,所以如果同步块中不加判断还是会出现负数,这个有点类似于单例模式的这种模型,为了在多线程下保证单例public class Test18 { static Object lock=new Object(); private static Test18 singleton=null; private Test18 (){} public static Test18 getInstance(){ if(singleton==null){ synchronized (lock) { if(singleton==null) singleton=new Test18(); } } return singleton; } }
所以修改同步块内容为
synchronized(this) { if(count>0) { output.write("车票"+(count--)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n"); System.out.println("车票"+(count)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n"); } }
二:用lock达到同样效果
class R extends Thread{static int count=1000;//火车票static long currenttime;static long endtime;int agent=0;static File file=new File("C:/Users/绝影/Desktop/log.txt");static OutputStream out=null;static OutputStreamWriter output=null;private Lock lock = new ReentrantLock();// 锁对象static {System.out.println("我是静态块");try {out=new FileOutputStream(file);output=new OutputStreamWriter(out);} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void one() throws IOException {// TODO 线程输出方法try {if(count>0) {lock.lock();// 得到锁if(count>0) {output.write("车票"+(count--)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n");System.out.println("车票"+(count)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n");}lock.unlock();// 释放锁}else{endtime=System.currentTimeMillis();System.out.println(endtime);System.out.println("抱歉现在没有车票了"+"总共用了时间"+(endtime-currenttime)+"卖完完余票");System.exit(0);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {output.flush();}}@Overridepublic void run() {try {try {sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}one();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
public class C {int count = 1000;// 火车票static long currenttime;static long endtime;public static void main(String[] args) throws InterruptedException {currenttime = System.currentTimeMillis();final R b = new R();System.out.println("当前时间" + currenttime);// 方式二for (int j = 1; j <= 1200; j++) {// 1000new Thread(b).start();}}}方法三:synchronized关键字加到方法上,这种只是锁的范围大,效率低
import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;class BB implements Runnable{static int count=1000;//火车票static long currenttime;static long endtime;int agent=0;static File file=new File("C:/Users/绝影/Desktop/log.txt");static OutputStream out=null;static OutputStreamWriter output=null;static {System.out.println("我是静态块");try {out=new FileOutputStream(file);output=new OutputStreamWriter(out);} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public synchronized void one() throws IOException {// TODO 线程输出方法try {if(count>0) {output.write("车票"+(count--)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n");System.out.println("车票"+(count)+"正在 "+Thread.currentThread().getName()+" 出售"+"\r\n");}else{endtime=System.currentTimeMillis();System.out.println(endtime);System.out.println("抱歉现在没有车票了"+"总共用了时间"+(endtime-currenttime)+"卖完完余票");System.exit(0);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {output.flush();}}@Overridepublic void run() {try {one();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}public class C{static long currenttime;static long endtime;public static void main(String[] args) throws InterruptedException {currenttime=System.currentTimeMillis();final BB e = new BB();System.out.println("当前时间"+currenttime);//方式三for(int j=1;j<=1200;j++) {//1000new Thread(e).start();}}}
相关文章推荐
- [代码安全] 关于全局变量被修改以及volatile的用法
- 在多线程情况下 局部变量与全局变量 哪个比较安全呢
- 多线程读一个全局变量要不要加锁?还是说只是当修改全局变量的时候才要加锁?
- c#多线程修改全局变量
- 在多线程情况下 局部变量与全局变量 哪个比较安全呢
- MySQL 8 新特性之持久化全局变量的修改
- 多线程共用全局变量可能存在的威胁
- QT 之 QMutexLocker如何安全锁住全局变量
- Xcode全局修改变量、方法名
- Python 全局变量修改--global
- 多线程对单例的局部变量和全局变量的影响
- 【python】多线程共享全局变量问题
- 多进程修改全局变量
- python 全局变量引用与修改
- Node.js全局安全位置修改
- 多年以后重发:多线程安全的变量模板
- 多线程安全的变量模板
- 一个.c文件中声明全局变量的结构体,另一个.c的文件中调用函数修改它_百度知道
- 多线程:利用互斥锁来处理全局变量的互斥问题