• 由一个多线程共享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();
                    }
                }
            }
        }
    }

  • 相关阅读:
    JAVA实现微信支付功能
    avue设置表格显示图片
    职工管理系统----删除职工
    职工管理系统---显示职工
    职工管理系统---读文件
    职工管理系统---写文件
    职工管理系统-------添加职工
    职工管理系统-----实现职工类
    职工管理系统-------实现退出功能
    职工管理系统-------菜单功能
  • 原文地址:https://www.cnblogs.com/luoxn28/p/6012973.html
Copyright © 2020-2023  润新知