由一个多线程共享Integer类变量问题引起的。。。

 最近看到一个多线程面试题,有三个线程分别打印A、B、C,请用多线程编程实现,在屏幕上循环打印10次ABCABC…

  看到这个题目,首先想到的是解决方法是定义一个Integer类对象,初始化为0,由3个线程共享,如果Integer对象取余3之后等于0,则打印A,同时进行加1操作;如果Integer对象取3之后等于1,则打印B,同时进行加1操作;如果Integer对象取3之后等于1,则打印C,如果循环打印了10次的话,就退出线程。

/**
 * ThreeThread
 * 3个线程测试
 */
public class ThreeThread {

    public static void main(String[] args) throws InterruptedException {
        Integer gData   = 0;
        Thread  thread1 = new MyTask(gData, 0, "A");
        Thread  thread2 = new MyTask(gData, 1, "B");
        Thread  thread3 = new MyTask(gData, 2, "C");

        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();
    }

}

class MyTask extends Thread {

    private Integer gData;
    private int     n;
    private String  info;

    public MyTask(Integer gData, int n, String info) {
        super("thread " + info);
        this.gData = gData;
        this.n     = n;
        this.info  = info;
    }

    public void run() {
        int i = 0;

        while (true) {
            synchronized (gData) {
                if (gData % 3 == n) {
                    System.out.print(info + " ");
                    gData++;
                    i++;
                }
            }

            if (i == 10) {
                break;
            }
            else {
                Thread.yield();
                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

运行程序结果如下:

  发现只有A线程打印了"A",并没有发现B线程和C线程打印字符串:(。难道是A线程更改了Integer对象的值,而B线程和C线程并没有“看到”更新后的值?于是,在线程类的run方法的while循环中增加代码如下:

while (true) {
    System.out.println(Thread.currentThread().getName() + " " + gData);
    synchronized (gData) {
        if (gData % 3 == n) {
            System.out.print(info + " ");
            gData++;
            i++;
        }
    }

    ...
}

运行程序结果如下:

  由运行结果可知,刚开始A、B、C线程都拥有Integer类变量,并且初值为0。当A线程更改Integer类变量为1时,但是B和C线程中的Integer类变量的值仍然为0,因此,结果肯定不会打印出ABCABC....

  通过阅读Integer类源码,可知Integer类中存放int值的变量类型是final的:

/**
 * The value of the {@code Integer}.
 *
 * @serial
 */
private final int value;

  也就是说,Integer类对象的值每更新一次,就会创建一个新的Integer对象。运行程序结果只打印出了"A",表示刚开始A、B、C线程都拥有同一个Integer类变量,并且初值为0,但是当A线程更新Integer对象的值后,A线程中的Integer对象和B/C线程中的Integer对象已经不是同一个对象了。

  为了能够正常打印出ABCABC字符串,可以把Integer对象类型改为AtomicInteger,代码如下:

/**
 * ThreeThread
 * 3个线程测试
 */
public class ThreeThread {

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger gData = new AtomicInteger(0);
        Thread  thread1 = new MyTask(gData, 0, "A");
        Thread  thread2 = new MyTask(gData, 1, "B");
        Thread  thread3 = new MyTask(gData, 2, "C");

        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();
    }

}

class MyTask extends Thread {

    private AtomicInteger gData;
    private int     n;
    private String  info;

    public MyTask(AtomicInteger gData, int n, String info) {
        super("thread " + info);
        this.gData = gData;
        this.n = n;
        this.info = info;
    }

    public void run() {
        int i = 0;

        while (true) {
            synchronized (gData) {
                if (gData.get() % 3 == n) {
                    System.out.print(info + " ");
                    gData.incrementAndGet();
                    i++;
                }
            }

            if (i == 10) {
                break;
            }
            else {
                Thread.yield();
                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

  第二种打印ABCABC...字符串的解决方法是使用wait/notify函数,示例代码如下:

/**
 * ThreeThread2
 * 三个线程依次输出A B C,使用线程同步方式
 */
public class ThreeThread2 {

    public static void main(String[] args) throws InterruptedException {
        Object A = new Object();
        Object B = new Object();
        Object C = new Object();

        MyThread myThread1 = new MyThread(C, A, "A");
        MyThread myThread2 = new MyThread(A, B, "B");
        MyThread myThread3 = new MyThread(B, C, "C");

        myThread1.start();
        Thread.sleep(10);
        myThread2.start();
        Thread.sleep(10);
        myThread3.start();

        try {
            myThread1.join();
            myThread2.join();
            myThread3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class MyThread extends Thread {
    private Object prev;
    private Object curr;
    private String info;

    public MyThread(Object prev, Object curr, String info) {
        this.prev = prev;
        this.curr = curr;
        this.info = info;
    }

    public void run() {
        int cnt = 10;

        while (cnt-- > 0) {
            synchronized (prev) {
                synchronized (curr) {
                    System.out.print(info + " ");
                    curr.notify();
                }

                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

时间: 2024-09-28 01:20:48

由一个多线程共享Integer类变量问题引起的。。。的相关文章

JAVA笔记14__多线程共享数据(同步)/ 线程死锁 /

/** * 多线程共享数据 * 线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行. * 多线程共享数据的安全问题,使用同步解决. * 线程同步两种方法: * 1.同步代码块 * synchronized(要同步的对象){ 要同步的操作 } * 2.同步方法 * public synchronized void method(){ 要同步的操作 } */ public class Main { public static void main(

并行编程之多线程共享非volatile变量,会不会可能导致线程while死循环

背景 大家都知道线程之间共享变量要用volatile关键字.但是,如果不用volatile来标识,会不会导致线程死循环?比如下面的伪代码: static int flag = -1; void thread1(){ while(flag > 0){ //wait or do something } } void thread2(){ //do something flag = -1; } 线程1,线程2同时运行,线程2退出之后,线程1会不会有可能因为缓存等原因,一直死循环? 真实的世界 第一个坑

线程系列03,多线程共享数据,多线程不共享数据

多线程编程,有时希望每个线程的数据相互隔离互不影响,有时却希望线程间能共享数据,并保持同步.本篇体验多线程共享和不共享数据. □ 多线程不共享数据 对于多线程,CLR到底是怎样为它们分配内存栈空间呢?是"一个萝卜一个坑",每个线程都有自己的栈空间:还是"大树底下好乘凉",所有的线程共享同一个栈空间? 我们让2个线程执行相同的静态方法,用到相同的变量,通过打印变量来求证多线程栈空间的分配情况. class Program { static void Main(stri

C++实现一个多线程同步方式的协同工作程序示例

多线程并发程序与协同程序其实是不同的概念.多线程并发是多个执行序同时运行,而协同程序是多个执行序列相互协作,同一时刻只有一个执行序列.今天想到的是将两者结合起来,拿现实生活中的例子来说,假设一个班级有100个学生,一个老师要批改100个学生的作业,有时老师太忙或者赶时间会叫几个同学帮忙批改,等所有同学都批改完后都交到老师手中,老师在下次上课的时候将作业本一起发给班上的学生....其实在并发编程的时候也可以借鉴这一个思想和模式,特别是网络服务器开发的过程中,并发与协同经常出现,于是今天写了一个简单

用 python 实现一个多线程网页下载器

今天上来分享一下昨天实现的一个多线程网页下载器. 这是一个有着真实需求的实现,我的用途是拿它来通过 HTTP 方式向服务器提交游戏数据.把它放上来也是想大家帮忙挑刺,找找 bug,让它工作得更好. keywords:python,http,multi-threads,thread,threading,httplib,urllib,urllib2,Queue,http pool,httppool 废话少说,上源码: 1 # -*- coding:utf-8 -*- 2 import urllib,

一个基于共享内存的ipc通信框架

一个基于共享内存的ipc通信框架 与共享内存相关的操作主要包括共享内存的初始化, 共享内存的释放, 共享内存的锁的相关操作, 在这里操作共享内存的环境是: 1 多个进程没有亲缘关系, 也没有server/client关系, 是多个不相关进程并发操作共享内存 2 共享内存一开始不存在, 由第一个访问他的进程创建 3 当共享内存退出时, 由最后一个离开的进程释放共享内存, 并清除信号量 在这个问题之中, 主要有两个比较大的问题: 1 怎样新建并初始化共享内存 新建共享内存的数据都可以用信号量来控制,

一个多线程问题引发的血案-(代码段执行完毕,子进程未执行完毕导致段错误)

今天遇到一个问题,gdb执行程序完全没有问题,但直接执行就会段错误,百思不得其解,各种纠结,各种搜索引擎都试了一遍,无果!后来问题还是被我自己挖出来了. 看下边一段代码: int TaskSendControl() { pthread_t prov_thread[CLIENT_NUM]; int prov[CLIENT_NUM]; for(int i=0; i< CLIENT_NUM; i++) { prov[i] = i; if( pthread_create(&prov_thread[i

车楚网 一个车辆共享的平台

车楚网 一个车辆共享的平台. 网址:http://www.chechuw.com/    百度直接搜索 车楚网 就是我们啦! (平台首页) 1.免费发布车辆租赁信息: 2.自己网上开车店,自由管理店铺,不需任何费用: 3.独立域名地址属于您.可直接浏览器输入您网店的地址.直接跳转,您还在等什么: 4.平台pv每天4000,高峰时刻20000,利用我们的优势帮助您的公司转型互联网,更您带来更多的客户: 5.我们正在招募合作租赁公司,邀您加入我们: 车楚网创立于2014年10月,主要为个人和租赁公司

车楚网 一个车辆共享的平台。

车楚网 一个车辆共享的平台. 网址:http://www.chechuw.com/    百度直接搜索 车楚网 就是我们啦! (平台首页) 1.免费发布车辆租赁信息: 2.自己网上开车店,自由管理店铺,不需任何费用: 3.独立域名地址属于您.可直接浏览器输入您网店的地址.直接跳转,您还在等什么: 4.平台pv每天4000,高峰时刻20000,利用我们的优势帮助您的公司转型互联网,更您带来更多的客户: 5.我们正在招募合作租赁公司,邀您加入我们: 车楚网创立于2014年10月,主要为个人和租赁公司