红迅+高新现代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里面去用它。
- 线程持有:比如我们有三个不同的类。在一次Web请求中,会在不同的地方,不同的时候,调用这三个类的实例。但用户是同一个,用户数据可以保存在「一个线程」里。
这样做的优势在于:持有线程资源供线程的各个部分使用,全局获取,降低「编程难度」。
- 线程一致:这里以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)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程
- 2024-11-26Springboot单体架构搭建资料:新手入门教程
- 2024-11-26Springboot单体架构搭建资料详解与实战教程
- 2024-11-26Springboot框架资料:新手入门教程
- 2024-11-26Springboot企业级开发资料入门教程
- 2024-11-26SpringBoot企业级开发资料详解与实战教程
- 2024-11-26Springboot微服务资料:新手入门全攻略