您的位置:首页 > 职场人生

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息