JAVA中,关于多线程的疑惑,求解答

孙博文
  • 35

JAVA中,关于多线程的疑惑

疑惑来源于网上文章的阅读,和自己的测试

来源于网上的测试代码

public class Runnable1 implements Runnable{
    private static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Runnable1());
            thread.start();
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("result: " + count);
    }

    @Override
    public void run() {
        synchronized (this){
            for (int i = 0; i < 10000; i++) {
                count++;
            }
        }

    }

}

描述

该文章是一个描述synchronized关键字的文章,文章说对于count这个共享变量,以synchronized来锁住当前对象保证并发访问的原子性,但是也让我产生了疑惑,为什么一定要访问本类中的变量,如果线程访问一个普通的类,比如user(举例而已)这样的java bean对象,由于java bean中的属性都是私有的,那么只能通过get/set方法访问,那么如果我锁住的是该bean的class对象是否可以保证,并发访问的正确性呢?

我做了测试,代码如下

public class Test {
    private static  int value = 0;

    public  static int getValue() {
        return value;
    }

    public  static void setValue(int value) {
        Test.value = value;
    }

    public static void main(String[] args) {
        ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("threadPool-%d").build();
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10),
                factory, new ThreadPoolExecutor.AbortPolicy());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        poolExecutor.shutdown();

        System.out.println(value);
    }

}

public class Thread1 extends Thread{

    @Override
    public void run() {

    synchronized (Thread.class){
        for (int i = 0; i < 100; i++) {
            int value = Test.getValue();
            ++value;
            Test.setValue(value);

        }
    }

    }

}

描述以及之后的修改

按照一样的思路,这里我修饰的是Test.class对象,测试结果不一致
之后尝试将bean的get/set方法也使用synchronized修饰,发现得到的结果依然不一致

开始怀疑自己的理解有问题

难道sychronized不是将访问的线程都自进入该对象时就加上锁吗???

求指点,万分感谢

回复
阅读 1.4k
3 个回答
一枚小白
  • 244
✓ 已被采纳

应该是两方面原因:
第一:你显式的使用了shutdown(),此时线程可能还在跑你就给干掉了,当然预期结果不一样
第二:即使不使用shutdown,你提交线程之后马上进行了System.out.println(value);此时预期结果也不一样,因为提交线程之后是异步去跑的,所以此时的主线程(就是这个main方法)不会等待之前的线程是否跑完就会直接进行输出了。所以预期结果不一样。

你可以再看看上面的demo代码,它有一个Thread.sleep(500),然后再输出;其实这个主线程等500ms的目的就是让子线程能够跑完,然后输出。假如demo代码的线程500ms跑不完,他的value结果也是不对的。你可以修改demo代码如下:主要修改是在run方法中加了Thread.sleep(1),保证这个线程在500ms跑不完。

public class Runnable1 implements Runnable{
    private static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Runnable1());
            thread.start();
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("result: " + count);
    }

    @Override
    public void run() {
        synchronized (this){
            for (int i = 0; i < 10000; i++) {
                count++;
                // 这是新增的代码,意识是:count加1之后休眠1ms
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

综上你把你的main代码修改如下看看,变动点有2个:

  1. 删除shutdown
  2. 在输出之前让主线程sleep一下,保证子线程执行完。
public static void main(String[] args) {
        ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("threadPool-%d").build();
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10),
                factory, new ThreadPoolExecutor.AbortPolicy());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        poolExecutor.execute(new Thread1());
        
        try {
            Thread.slepp(2000);
        } catch (InterruptedException e){
            
        }

        System.out.println(value);
    }

后面这段代码?哪里加锁了?
synchronized的目的是保证代码块的原子性操作

以synchronized来锁住当前对象保证并发访问的原子性,但是也让我产生了疑惑,为什么一定要访问本类中的变量,
这句话就很莫名?完全看不懂你想问什么

锁一个全局变量并没有问题, 问题是

poolExecutor.shutdown();

线程都还没执行完呢,你直接把它干掉了,输出能对么

宣传栏