红迅+高新现代Java笔试题(ThreadLocal)

2021/5/3 20:27:42

本文主要是介绍红迅+高新现代Java笔试题(ThreadLocal),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

面的是一家自研产品的公司,看了公司背景里面的开发人员居多都是985,211毕业的。然后做了份笔试题记录一下没做出和写错的题目。

一、两个线程,一个线程打印数字123456,一个线程打印字母ABCDEF,交替打印出1A2B3C

解题思路:
1.t1线程,打印数字1,然后唤醒t2线程
2.t1线程睡眠
3.t2线程打印字母A,然后唤醒t1线程
.
.
.
重复执行。

  • 首先用LockSupport.unpark()LockSupport.park() 的方案实现,t1线程必须相互持有,实现代码如下:
public class test {
    static String s1= "123456";
    static String s2= "ABCDEFG";
    public static void main(String[] args) {
        Thread thread1= null;
        Thread thread2 = null;
        MyRunnable myRunnable2 = new MyRunnable();
        MyRunnable myRunnable1 = new MyRunnable();
        thread1 = new Thread(myRunnable2);
        thread2 = new Thread(myRunnable1);
        myRunnable2.setOther(thread2);
        myRunnable2.setS(s2);
        myRunnable1.setOther(thread1);
        myRunnable1.setS(s1);
        thread2.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread1.start();
    }
    static class MyRunnable implements Runnable{
        private Thread other;
        private String s;
        public void setOther(Thread other) {
            this.other = other;
        }
        public void setS(String s) {
            this.s = s;
        }
        @Override
        public void run() {
            for(int i=0;i<s.length();i++){
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.err.print(s.charAt(i));
                LockSupport.unpark(other);
                LockSupport.park();
            }
        }
    }
}

这个方案,就是两个线程互相持有,线程的频繁挂起,阻塞,比较消耗系统资源。因为这里其实只是少量的打印,其实看不出什么影响。

第二种:用Synchronize+ wait() + notifyAll(), 其实现代码如下:

public class Test8 {
    static String s1 = "123456";
    static String s2 = "ABCDEFG";

    public static void main(String[] args) throws InterruptedException {
        Object lock1 = new Object();
        MyRunable r1 = new MyRunable(lock1, s1);
        MyRunable r2 = new MyRunable(lock1, s2);
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
    static class MyRunable implements Runnable {
        private Object mylock;
        private String s;
        public MyRunable(Object mylock, String s) {
            this.mylock = mylock;
            this.s = s;
        }
        @Override
        public void run() {
            synchronized (mylock) {
                for (int i = 0; i < s.length(); i++) {
                    System.out.println(s.charAt(i));
                    try {
                        mylock.notifyAll();
                        mylock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

二、除了new创建对象还能用什么办法。请用代码写出

  • 反射机制-Class对象的newInstance()方法;通过Class.forName()动态的加载类的Class对象,然后通过newInstance()方法获得Test类的对象
 String className = "org.b3log.solo.util.Test";
 Class classz = Class.forName(className);
 Test t  = (Test) classz.newInstance();
  • Object对象的clone()方法
  public static void main(String[] args) throws Exception {
    Test t1 = new Test("张三");
    Test t2 = (Test) t1.clone();
    System.out.println(t2.getName());
}

四、ThreadLocal是什么以及其作用场景(好文章)

  • ThreadLocal为每个使用变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不是影响其他线程所对应的副本。即一个线程局部变量在多个线程中,分别有独立的值(副本)。

是不是有些看不懂?我刚开始了解ThreadLocal的时候,也有点不明白这句话是什么意思。最大的疑惑是:既然是每个线程独有的,那我干嘛不直接在调用线程的时候,在相应的方法里面声明和使用这个局部变量?

后来才明白,同一个线程可能会调用到很多不同的类和方法,你可能需要在不同的地方,用到这个变量。

ThreadLocal是一个可以开箱即用、无额外开销、线程安全的工具类,可以完美解决这个问题。
ThreadLocal并不是Java独有的,在几乎所有提供多线程特征的语言里面,都会有ThreadLocal的实现。在Java中,ThreadLocal中用哈希表来实现的。
在这里插入图片描述
ThreadLocal的线程模型
其实并不复杂。左边的黑色大圆圈代表一个进程。进程里有一个线程表,红色波浪线代表一个个线程。

对于每个线程来说,都有自己的独占数据。这些独占数据是进程来分配的,对于Java来说,独占数据很多都是在Thread类里面分配的,而每一个线程里面都有一个ThreadLocalMap的对象,它本身是一个哈希表,里面会放一些线程的局部变量(红色长方形)。ThreadLocal的核心也是这个ThreadLocalMap。

  • 在实际项目中,ThreadLocal一般用来做什么呢?这里总结四种核心的应用场景。
    • 线程持有:比如我们有三个不同的类。在一次Web请求中,会在不同的地方,不同的时候,调用这三个类的实例。但用户是同一个,用户数据可以保存在「一个线程」里。
      在这里插入图片描述
      线程资源持有
      这个时候,我们可以在程序1把用户数据放进ThreadLocalMap里,然后在程序2和程序3里面去用它。

这样做的优势在于:持有线程资源供线程的各个部分使用,全局获取,降低「编程难度」。

  • 线程一致:这里以JDBC为例。我们经常会用到事务,它是怎么实现的呢?
    在这里插入图片描述
    线程资源一致性
    原来,我们每次对数据库操作,都会走JDBC getConnection,JDBC保证只要你是同一个线程过来的请求,不管是在哪个part,都返回的是同一个链接。这个就是使用ThreadLocal来做得。

当一个part过来的时候,JDBC会去看ThreadLocal里是不是已经有这个线程的链接了,如果有,就直接返回。如果没有,就从连接池请求分配一个链接,然后放进ThreadLocal里。

这样就可以保证一个事务的所有part都在一个连接里。TheadLocal可以帮助它维护这种一致性。

线程安全
假如我们一个线程的调用链路比较长。在中途中出现异常怎么做?我们可以在出错的时候,把错误信息放到ThreadLocal里面,然后再后续的链路中去使用这个值。使用ThreadLocal可以保证多个线程在处理
在这里插入图片描述
并发计算
在这里插入图片描述
如果我们有一个大的任务,可以把它拆分成很多小任务,分别计算,然后最终把结果汇总起来。如果是分布式计算,可能是先存储在自己的节点里。而如果是单机下的多线程计算,可以把每个线程的计算结果放进ThreadLocal里面,最后取出来汇总。

三、请写出List的全部遍历方式

  • 普通的for循环
for(int i=0;i<list.size();i++){
   list.get(i);
}
  • 迭代器遍历
    Iterator iterator = list.iterator();
    while(iterator.hasNext()){
    String item = iterator.next();
    }
  • 增强for循环
for(String s:list){
    System.out.println("item = " + s);
}
  • forEach遍历
list.forEach(item->{
     System.out.println("item = " + item);
});

五、Map的所有遍历方式

  • for循环
for(Map.Entry<String,Object> entry: map.entrySet()){}
        String key = entry.getKey().toString();
        String value = entry.getValue().toString();
  • 迭代器
Iterator it = tempMap.entrySet().iterator();
while(it.hasNext()){
   Map.Entry entry = (Map.Entry) it.next();
   Object key = entry.getKey();
   Object value = entry.getValue();
}
  • keySet()遍历
    for(String o:tempMap.keySet()){
    System.out.println(“key=” + o + " value=" + tempMap.get(o));
    }

六、构造方法的作用,能够被重写吗

  • 构造方法是一个与类名同名并且没有返回值类型的方法。对象的创建就是通过构造方法来完成,其功能就是①构造出来一个类的示例 ②完成对象的初始化,当类实例化一个对象时就会自动调用构造方法。构造方法和其他方法一样也可以重载。不能被重写。

七、Java处理异常的方式有哪些?为什么处理异常?

  • try…catch(){}finally{}
  • throws new MyException();

八、重写和重载,重载的作用

  • 重写,即继承父类,子类继承方法并重写其方法。
  • 重载,即和类名相同的方法名,但根据参数不同实现根自动调用对应的函数。

九、Bean的作用域,以及其单例模式安全吗(推荐)

  • 单例Singleton:即IOC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象。
  • prototype原型模式:每次通过Spring容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态,而Singleton全局只有一个对象。根据经验,对有状态的bean使用prototype作用域,而对无状态的bean使用singleton作用域。
  • request:在一次Http请求中,容器会返回该Bean的同一实例。而对不同的Http请求则会产生新的Bean,而且该bean仅在当前http request内有效。
  • session:在一次http session中,容器会返回该Bean的同一实例。而对不同的Session请求则会创建新的实例,该bean实例仅当前Session内有效
  • global Session:在一个全局的Http Session中,容器会返回该Bean的同一个实例,仅在使用portlet context时有效。

关于其单例是否线程安全?

Spring框架中的bean,或者说是组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候需要尤其注意的地方。
单例模式的意思是只有一个实例,例如在Spring容器中某一个类只有一个实例,而且自行实例化后并项整个系统提供这个实例,这个类称为单例类。

  • 对原型模式的bean,每次都会创建一个新对象,也就是线程之间并不存在Bean共享,不会有线程安全的问题。
  • 对于单例bean,所有线程都共享一个单例实例bean,因此是存在资源的竞争。
    • 如果单例beam,是一个无状态的bean,也就是线程中的操作不会对bean的成员执行查询以外的操作,那么这个单例bean是线程安全的
    • 比如spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
      对于有状态的bean,spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法。比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。
  • 使用ThreadLocal的好处是使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。

总结

  • 在@Controller/Service/Repository等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的。
  • 尽量不要再@Controller/Service/Repository等容器中定义静态变量,不论是在单例还是多里。她都是线程不安全的。
  • 对有状态的bean(有实例变量的对象,可以保存数据)要使用prototype
  • 作用域对无状态的bean(没有实例变量的对象,不能保存数据,是不变类,相当于静态)使用singleton作用域

十、Java 8的新特性

  • Lambda表达式

1.首先看看老版本的Java中是如何排序字符串的:

 List<String> list = Arrays.asList("peter","anna","mike");
        ArrayList<String> arrayList= new ArrayList<>();
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
只需要给静态方法 Collections.sort 传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。

lambda表达式:
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});

十一、List的实现类有哪些

  • ArrayList
  • LinkedList
  • Vector:线程安全
  • Vactor:底层是动态数组结构,线程不安全。
  • stack:继承于Vector,实现一个后进先出的堆栈。
  • CopyOnWriteArrayList


这篇关于红迅+高新现代Java笔试题(ThreadLocal)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程