今天去面试,碰到一个笔试题,自己想的和实际结果不一样,求各位大神给分析分析。

hong_369 2019-08-17 12:06:08
今天去面试,碰到一个笔试题,自己想的和实际结果不一样,求各位大神给分析分析。

原题:
public void delete(List<String> strs){
strs.add("123");
strs.add("456");
strs.add("789");
for(int i = 0; i < strs.size(); i++){
strs.remove(i);

}
System.out.println(strs.size());
}

问题:strs为ArrayList类型的实例变量,写出该代码在多线程下的输出结果。

我最初的想法是:由于strs是ArrayList实例入参,那么它应该是一个线程不安全的公共数据集合,在多线程下,它的size随着add的调用可能为任何值,所以我判断输出结果可能为:0、1、2、3...中任何值。毫无疑问,我错了,回来自己敲代码运行了下,懵逼了 结果是固定值 1、1、1、1、


import java.util.ArrayList;
import java.util.List;

public class Test1 extends Thread{

List<String> strs = new ArrayList<>();
public static void main(String args[]){
for (int i = 0; i < 100; i++){
new Test1().start();
}
}
@Override
public void run() {
delete(strs);
}
public void delete(List<String> strs){
strs.add("123");
strs.add("456");
strs.add("789");
for(int i = 0; i < strs.size(); i++){
strs.remove(i);

}
System.out.println(strs.size());
}
}



...全文
3679 62 打赏 收藏 转发到动态 举报
写回复
用AI写文章
62 条回复
切换为时间正序
请发表友善的回复…
发表回复
马克凸文 2019-08-27
  • 打赏
  • 举报
回复
引用 8 楼 着急下班的回复:
重点:实例变量。实例变量的特点是只属于当前对象。你new了多少个线程就有多少个不同的list对象,每个list里只有3个元素, 所以最后打印出来的都是1
还是你说的靠谱,其他的根本都没有仔细看题目
  • 打赏
  • 举报
回复
引用 69 楼 人到中年就秃头的回复:
情况有很多种,单线程应该是个公式,看自己摸索,我也不知道哪个公式,多线程应该是个范围

package com.example.springboot;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @作者: 杨得印
 * @创建时间: 2019/8/10
 * @描述: Test
 */
public class Test {
    public static void main(String[] args) {
        List<String> strs=new ArrayList<>();
        strs.add("1");
        strs.add("2");
        strs.add("3");
        strs.add("4");
        ExecutorService executorService=Executors.newFixedThreadPool(5);
        for (int i=0;i<10;i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    new Test().delete(strs);
                }
            });
        }


    }

    public void delete(List<String> strs){
        strs.add("123");
        strs.add("456");
        strs.add("789");
        for (int i = 0; i < strs.size(); i++) {
            strs.remove(i);
        }
        System.out.println(Thread.currentThread().getName()+"="+strs.size());
    }

}

这道题,还牵扯到线程优化(重点),这个题目不合理,如果要举出所有可能,并一一解释。1张a4纸压根就不行
  • 打赏
  • 举报
回复
情况有很多种,单线程应该是个公式,看自己摸索,我也不知道哪个公式,多线程应该是个范围

package com.example.springboot;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @作者: 杨得印
 * @创建时间: 2019/8/10
 * @描述: Test
 */
public class Test {
    public static void main(String[] args) {
        List<String> strs=new ArrayList<>();
        strs.add("1");
        strs.add("2");
        strs.add("3");
        strs.add("4");
        ExecutorService executorService=Executors.newFixedThreadPool(5);
        for (int i=0;i<10;i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    new Test().delete(strs);
                }
            });
        }


    }

    public void delete(List<String> strs){
        strs.add("123");
        strs.add("456");
        strs.add("789");
        for (int i = 0; i < strs.size(); i++) {
            strs.remove(i);
        }
        System.out.println(Thread.currentThread().getName()+"="+strs.size());
    }

}

x84139526 2019-08-25
  • 打赏
  • 举报
回复
引用 67 楼 x84139526的回复:
第一次循环,i是0,stars. size是2,循环条件为true,strs移出一个(789)。第二次循环,i是1,strs. size是1,循环条件为false,不执行循环体既跳出循环。然后执行打印语句,此时stars. size大小为1,也就是有两个数据。
在线程保护的情况下,这个方法体里面的变量生命周期只限于这个方法体,所以每次开启下一个线程都需要上一个线程执行完毕,因此每次执行方法体里面的变量时都是上一个线程释放资源后新创建的,所以每次结果都会一样
x84139526 2019-08-25
  • 打赏
  • 举报
回复
第一次循环,i是0,stars. size是2,循环条件为true,strs移出一个(789)。第二次循环,i是1,strs. size是1,循环条件为false,不执行循环体既跳出循环。然后执行打印语句,此时stars. size大小为1,也就是有两个数据。
缌唸 2019-08-22
  • 打赏
  • 举报
回复
应该在remove以后让i--
标2013 2019-08-21
  • 打赏
  • 举报
回复
抱着学习的心态来看看
Lazy… 2019-08-21
  • 打赏
  • 举报
回复
菜鸟:集合 会随着删除 减小吗?
꧁风信子꧂ 2019-08-21
  • 打赏
  • 举报
回复
看着很懵,初学小白
  • 打赏
  • 举报
回复
引用 17 楼 你是小KS的回复:
[quote=引用 16 楼 hong_369的回复:]总感觉这道笔试题,描述的不清晰,这个入参strs没有清楚的告诉是否是一个共享数据,很容易给人误判,两种思路下的结果是截然不同的
要是告诉你了,不就告诉你答案了[/quote] 是没有删除第二个元素456
  • 打赏
  • 举报
回复
集合会出现下标补位,就这么简单,,线程资源没有调度,list要补位要比remove()方法快,第二个删除不到
枭. 2019-08-21
  • 打赏
  • 举报
回复
删除123后,456变成元素0,789变成元素1,此时i为1,删除了789,还剩下456
燕昭市骏 2019-08-21
  • 打赏
  • 举报
回复
初学,一起分析看看!
ycheang 2019-08-20
  • 打赏
  • 举报
回复
关键点在于,你的对象是在线程里面new还是在线程外面new
pstrunner 2019-08-20
  • 打赏
  • 举报
回复
引用 44 楼 iicup 的回复:
这个题目里面, strs是局部变量, 所以和它是不是多线程没有关系. 多线程只是用来干扰你判断的, 重点是考你 remove
正解
菜鸟①个 2019-08-20
  • 打赏
  • 举报
回复
引用 48 楼 月光如约而至的回复:
想找3分来下载PS,是不是回复了就有分啊
回复没有分的,ps网上资源多的很,为什么要在csdn下。。。。
9 Tribez 2019-08-20
  • 打赏
  • 举报
回复
多线程情况下 应该是对公共资源进行争抢,你测试代码里没次都有新的 list出来,也就不符合多线程了。你把他放外面,结果自然是随机的。
9 Tribez 2019-08-20
  • 打赏
  • 举报
回复
hello?在吗 等会 我知道为什么了
bothAD 2019-08-20
  • 打赏
  • 举报
回复
引用 41 楼 大·风的回复:
我来解释下你上面的问题: 1、你认为是线程不安全的,但是实际上为什么不是。
List<String> strs = new ArrayList<>();
new Test1()
这种写法其实你是为每一个线程创建了一个List,实际上对每个线程的List来说实现了线程封闭,虽然集合本身是线程不安全的,但是在线程封闭情况下是线程安全的。假如使用static或者线程接收一个List参数,然后在外部创建List再去调用,这个这个就是线程不安全的了,具体可能输出多少这个和你开的线程数和机器并发性能有关。 2、为什么会输出1

for (int i = 0; i < strs.size(); i++) {
            strs.remove(i);
}
使用这种循环意味着你再循环过程中在不断调整i的上限。当第一次执行的时候I的上线被调整到2了。所以第二次执行完循环就结束了。所以最后一个值并没有被移除,所以输出会为1。 另外使用这种循环会报错的,具体是循环的时候集合数据会越来越少,I会超过集合边界。

int index = strs.size()
for (int i = 0; i < index; i++) {
            strs.remove(i);
}
这道题感觉还不错,主要考验了你多线程和循环操作的一些小知识点。
你一说就一下醒悟了。。。
bothAD 2019-08-20
  • 打赏
  • 举报
回复
引用 41 楼 大·风的回复:
我来解释下你上面的问题: 1、你认为是线程不安全的,但是实际上为什么不是。
List<String> strs = new ArrayList<>();
new Test1()
这种写法其实你是为每一个线程创建了一个List,实际上对每个线程的List来说实现了线程封闭,虽然集合本身是线程不安全的,但是在线程封闭情况下是线程安全的。假如使用static或者线程接收一个List参数,然后在外部创建List再去调用,这个这个就是线程不安全的了,具体可能输出多少这个和你开的线程数和机器并发性能有关。 2、为什么会输出1

for (int i = 0; i < strs.size(); i++) {
            strs.remove(i);
}
使用这种循环意味着你再循环过程中在不断调整i的上限。当第一次执行的时候I的上线被调整到2了。所以第二次执行完循环就结束了。所以最后一个值并没有被移除,所以输出会为1。 另外使用这种循环会报错的,具体是循环的时候集合数据会越来越少,I会超过集合边界。

int index = strs.size()
for (int i = 0; i < index; i++) {
            strs.remove(i);
}
这道题感觉还不错,主要考验了你多线程和循环操作的一些小知识点。
循环会报错还真没发现
加载更多回复(42)

51,409

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧