面的是一家自研产品的公司,看了公司背景里面的开发人员居多都是985,211毕业的。然后做了份笔试题记录一下没做出和写错的题目。
解题思路:
1.t1线程,打印数字1,然后唤醒t2线程
2.t1线程睡眠
3.t2线程打印字母A,然后唤醒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(); } } } } } }
String className = "org.b3log.solo.util.Test"; Class classz = Class.forName(className); Test t = (Test) classz.newInstance();
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并不是Java独有的,在几乎所有提供多线程特征的语言里面,都会有ThreadLocal的实现。在Java中,ThreadLocal中用哈希表来实现的。
ThreadLocal的线程模型
其实并不复杂。左边的黑色大圆圈代表一个进程。进程里有一个线程表,红色波浪线代表一个个线程。
对于每个线程来说,都有自己的独占数据。这些独占数据是进程来分配的,对于Java来说,独占数据很多都是在Thread类里面分配的,而每一个线程里面都有一个ThreadLocalMap的对象,它本身是一个哈希表,里面会放一些线程的局部变量(红色长方形)。ThreadLocal的核心也是这个ThreadLocalMap。
这样做的优势在于:持有线程资源供线程的各个部分使用,全局获取,降低「编程难度」。
当一个part过来的时候,JDBC会去看ThreadLocal里是不是已经有这个线程的链接了,如果有,就直接返回。如果没有,就从连接池请求分配一个链接,然后放进ThreadLocal里。
这样就可以保证一个事务的所有part都在一个连接里。TheadLocal可以帮助它维护这种一致性。
线程安全
假如我们一个线程的调用链路比较长。在中途中出现异常怎么做?我们可以在出错的时候,把错误信息放到ThreadLocal里面,然后再后续的链路中去使用这个值。使用ThreadLocal可以保证多个线程在处理
并发计算
如果我们有一个大的任务,可以把它拆分成很多小任务,分别计算,然后最终把结果汇总起来。如果是分布式计算,可能是先存储在自己的节点里。而如果是单机下的多线程计算,可以把每个线程的计算结果放进ThreadLocal里面,最后取出来汇总。
for(int i=0;i<list.size();i++){ list.get(i); }
for(String s:list){ System.out.println("item = " + s); }
list.forEach(item->{ System.out.println("item = " + item); });
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(); }
关于其单例是否线程安全?
Spring框架中的bean,或者说是组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候需要尤其注意的地方。
单例模式的意思是只有一个实例,例如在Spring容器中某一个类只有一个实例,而且自行实例化后并项整个系统提供这个实例,这个类称为单例类。
总结
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);
});