java面试-线程知识点汇总
2018-01-18 14:03
330 查看
1. thread.join()作用
很直观解释就是凡是代码处执行了treadA.join()方法,那么下面的代码必须等待treadA执行完下面代码才可以执行代码
public class JoinTest { public static void main(String args[]) throws InterruptedException { Thread threadA = new Thread(new Runnable() { @Override public void run() { PrintUtil.printString("线程A执行完毕"); } }); threadA.start(); threadA.join(); PrintUtil.printString("主线程执行完毕"); } }
执行结果
线程A执行完毕 主线程执行完毕
由此可见必须等threadA执行完才会继续向下执行
2.Java中Runnable和Callable有什么不同?
Callablle允许又返回结果, runnale没有代码
public class CallableCompareRunnableTest { public static void main(String args[]) throws ExecutionException, InterruptedException { Callable<String> callable = new Callable() { @Override public Object call() throws Exception { return "《callable 执行完毕》"; } }; Runnable runnable = new Runnable() { @Override public void run() { PrintUtil.printString("runnable 执行没有返回结果且不抛异常"); } }; // 可以考虑FutureTask, Future的关系 FutureTask<String> task = new FutureTask<>(callable); // runnable直接执行run是不会是新的线程 new Thread(runnable).start(); new Thread(task).start(); // get()方法会阻塞线程,下面执行的代码必须等该线程执行完成 String result = task.get(); PrintUtil.printString("FutureTask执行完毕返回结果:" + result); PrintUtil.printString("主线程执行完毕"); } }
3.ThreadLocal
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。实现起来也比较简单,我就建立一个Map, key为线程,值为对应的值即可那么每个线程对应的变量就是独立的了。使用场景
都是配合static使用的,下面有一个例子,会阐述为什么这样用
代码
public class ThreadLocalTest { public static void main(String [] args ) throws IOException { final Thread threadA = new Thread(new Runnable() { @Override public void run() { Student student = new Student("小强", "男"); StudentCountHolder.studentCountHolder = new ThreadLocal<>(); StudentCount studentCount = new StudentCount(); studentCount.addMale(); StudentCountHolder.studentCountHolder.set(studentCount); PrintUtil.printString("threadA, 设置男生总数是:" + StudentCountHolder.studentCountHolder.get().getMaleCount()) ; try { Thread.sleep(2000); int maleCount = StudentCountHolder.studentCountHolder.get().getMaleCount(); int femaleCount = StudentCountHolder.studentCountHolder.get().getFemaleCount(); PrintUtil.printString("threadA, 获取男生总数是:" + maleCount) ; PrintUtil.printString("threadA, 获取女生总数是:" + femaleCount) ; } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { StudentCount studentCount = new StudentCount(); Student student = new Student("小芳", "女"); studentCount.addFemale(); Student student2 = new Student("小军", "男"); studentCount.addMale(); Student student3 = new Student("小明", "男"); studentCount.addMale(); Student student4 = new Student("小月", "女"); studentCount.addFemale(); StudentCountHolder.studentCountHolder = new ThreadLocal<>(); StudentCountHolder.studentCountHolder.set(studentCount); PrintUtil.printString("threadB, 设置男生总数是:" + StudentCountHolder.studentCountHolder.get().getMaleCount()) ; PrintUtil.printString("threadB, 设置女生总数是:" + StudentCountHolder.studentCountHolder.get().getFemaleCount()) ; } }); threadA.start(); threadB.start(); } } 运行结果 threadA, 设置男生总数是:1 threadB, 设置男生总数是:2 threadB, 设置女生总数是:2 threadA, 获取男生总数是:1 threadA, 获取女生总数是:0 public class StudentCount { private int maleCount; private int femaleCount; public void addMale(){ this.maleCount ++; } public void addFemale(){ this.femaleCount ++; } public int getMaleCount() { return maleCount; } public void setMaleCount(int maleCount) { this.maleCount = maleCount; } public int getFemaleCount() { return femaleCount; } public void setFemaleCount(int femaleCount) { this.femaleCount = femaleCount; } }
解释一下代码StudentCount 统计了学生的男女数量,我们用户StudentCountHodler持有了这个统计信息,然后其他地方可以随便调用。那么问题来了为什么要用ThreadLoal?传统方法我是应该直接就用代码中new 的studentCount就可以了,那么问题来了,假设我的程序中再来个学校的类,再来个教导处的类,且都需要知道学生的统计信息,那么是不是要给学校和教导处都复制一个统计信息,有了threadLocal就不需要直接调用就可以了。
4.CyclicBarrier,CountDownLatch, Semaphore
CyclicBarrier结合代码分析实现所有的线程一起等待一个操作,当这个操作发生时,所有线程一起开始往下执行的。有点类似,所有线程都await(), 当事件出发了notifyAll();下面的例子也很好理解,我们自动化部署的时侯多台服务器的时候(需要所有服务必须先停的情况),所有服务停止了,更新脚本(也必须在服务停止好,否则有可能出现异常),部署。
public class CyclicBarrierTest { static AtomicBoolean isSqlUpdated = new AtomicBoolean(false); public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(4); // 下面模拟APP服务器(负载2台)更新代码操作 new MyThread(barrier,"APP服务器1").start(); new MyThread(barrier,"APP服务器2").start(); new MyThread(barrier,"APP服务器3").start(); new MyThread(barrier,"APP服务器4").start(); } static class MyThread extends Thread { private String serveName; private CyclicBarrier cyclicBarrier; public MyThread(CyclicBarrier cyclicBarrier, String serveName) { this.cyclicBarrier = cyclicBarrier; this.serveName = serveName; } @Override public void run() { System.out.println(serveName + "打包上传成功"); System.out.println(serveName + "正在停止服务"); System.out.println(serveName + "正在进行备份操作"); try { System.out.println(serveName + "服务停止完毕"); // 必须等待其他机器都停止了,才能执行新的脚本 cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } if (isSqlUpdated.compareAndSet(false, true)) { System.out.println(serveName + "执行数据库脚本更新"); } System.out.println(serveName + "继续执行部署操作"); } } }
CountDownLatch在多个线程执行任务时候一个线程(例子中是主线程)等待其他线程(一个或多个线程)执行完在执行。下面场景也很常见,一群测试人员等待测试(主线程),俩名开发人员正在提交最新代码(2个线程)提交完,测试人员(主线程)部署测试。 关于这个实现类似于多个线程join()方法,也有点像多个Future.get()
public class CountDownLatchTest { public static void main(String[] args) { System.out.println("等待小明,小强提交代码..."); final CountDownLatch latch = new CountDownLatch(2); // 一个项目2个人一起协作,完成之后,要提交到SVN,最后打包部署到测试环境 new MyThread(latch, "小明").start(); new MyThread(latch, "小强").start(); try { latch.await(); System.out.println("小明,小强提交代码完毕"); System.out.println("可以用jenkins打包部署了"); } catch (InterruptedException e) { e.printStackTrace(); } } static class MyThread extends Thread{ final CountDownLatch latch; private String name; public MyThread (CountDownLatch latch, String name){ this.latch = latch; this.name = name; } @Override public void run() { System.out.println(name + "提交代码完毕"); latch.countDown(); } } }
Semaphore: 这个倒是好举例子,共享单车都知道吧,比如你在XX小区,附近一共就10辆,但是呢汽车的人何止10人,这就有人骑,有人等,等的人就是等待有人用完。
Semaphore的acquire()方法的意思就是说我要骑共享单车了,若是有我则直接骑走,没有我在这里等有了(就是别人骑完了release())再骑(实际情况你当然也可以选择我不骑了)
public class SemaphoreTest { public static void main(String[] args) { int computer = 10; Semaphore semaphore = new Semaphore(10); // 20个小学生在排队上网 for(int i = 0; i < 20; i++){ new MyThread(i, semaphore).start(); } } static class MyThread extends Thread{ private int num; private Semaphore semaphore; public MyThread(int num,Semaphore semaphore){ this.num = num; this.semaphore = semaphore; } @Override public void run() { try { String name = "小学生" + this.num; semaphore.acquire(); System.out.println(name + "抢到了一台电脑在玩LOL"); System.out.println("正在模拟" + name + "上网"); System.out.println(name + "正在上网,很嗨"); Thread.sleep(2000); System.out.println("网管监控到小学生" + this.num + "已经玩了1小时,时间到了"); System.out.println("小学生" + this.num + "上午时间到了2块钱没了小学生下机了"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
5.线程同步机制
内部锁和显式锁Synchronized 隐式锁
public class SynchronizedTest { private String shareString = "我"; static SynchronizedTest synchronizedTest = new SynchronizedTest(); public static void main(String [] args ) throws IOException { int threadNum = 10; for (int i = 0; i < threadNum; i++) { new MyThread("thread" + i, synchronizedTest).start(); } } // 修饰静态方法 public synchronized void synchronizedStaticMethodTest(String name) { PrintUtil.printString(name + "在执行synchronizedStaticMethodTest 第一次"); PrintUtil.printString(name + "在执行synchronizedStaticMethodTest 第二次"); } // 修饰普通方法 public synchronized void synchronizedMethodTest(String name) { PrintUtil.printString(name + "在执行SynchronizedMethodTdest 第一次"); PrintUtil.printString(name + "在执行SynchronizedMethodTest 第二次"); } // 修饰块 public void synchronizedMethodBlockTest(String name){ synchronized (shareString){ PrintUtil.printString(name + "在执行synchronizedMethodBlockTest 第一次"); PrintUtil.printString(name + "在执行synchronizedMethodBlockTest 第二次"); } } static class MyThread extends Thread { private String name; /** * 1。共享的实例synchronizedTest SynchronizedMethodTest方法同一事件只允许一个线程实例执行 * 因此执行结果可以预见俩个打印方法一定是执行完,其他线程才能进入 * 2。和修饰方法类似 */ SynchronizedTest synchronizedTest; public MyThread(String name, SynchronizedTest synchronizedTest) { this.name = name; this.synchronizedTest = synchronizedTest; } @Override public void run (){ try { synchronizedTest.synchronizedStaticMethodTest(name); Thread.sleep(3000); synchronizedTest.synchronizedMethodTest(name); // 便于看下面结果测试 Thread.sleep(3000); synchronizedTest.synchronizedMethodBlockTest(name); } catch (InterruptedException e) { e.printStackTrace(); } } } }
显式锁
public class LockTest { private String shareString = "wo"; private Lock lock = new ReentrantLock(); final static LockTest test = new LockTest(); private void lockMethodTest(String name) { lock.lock(); try { System.out.println(name + "得到了锁"); } catch (Exception e) { } finally { System.out.println(name + "释放了锁"); lock.unlock(); } } public static void main(String[] args) { int threadNum = 10; for (int i = 0; i < threadNum; i++) { new MyThread("thread" + i).start(); } } static class MyThread extends Thread { private String name; public MyThread(String name) { this.name = name; } @Override public void run() { test.lockMethodTest(name); } } }
CAS(Compare And Set)
volidate
相关文章推荐
- java面试知识点汇总
- java面试知识点汇总1
- 【Java面试知识点】Java面试知识点汇总
- java面试知识点汇总2
- java 面试知识点汇总
- Java中线程的相关知识点汇总
- [Java笔试面试知识点自整理系列]Java线程中run和start方法的区别
- java面试知识点汇总3
- java知识点汇总之十四线程
- java面试知识点汇总4
- 【面试之java基础】小知识点汇总
- java面试知识点汇总(想拿高工资的快来看看)
- Java 面试知识点汇总
- 【Java面试知识点】各大公司Java面试题目汇总-Java基础知识
- Java多线程面试知识点汇总(超详细总结)
- JAVA面试知识点
- 125条常见的java面试笔试题大汇总
- java面试题目汇总01
- Java面试笔试知识点之基础部分3
- Java面试笔试题大汇总(一)