java面试-线程知识点汇总

1. thread.join()作用

  1. 很直观解释就是凡是代码处执行了treadA.join()方法,那么下面的代码必须等待treadA执行完下面代码才可以执行
  2. 代码
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("主线程执行完毕");
    }
}
  1. 执行结果
 线程A执行完毕
 主线程执行完毕

由此可见必须等threadA执行完才会继续向下执行

2.Java中Runnable和Callable有什么不同?

  1. Callablle允许又返回结果, runnale没有
  2. 代码
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

  1. 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。实现起来也比较简单,我就建立一个Map, key为线程,值为对应的值即可那么每个线程对应的变量就是独立的了。
  2. 使用场景
    都是配合static使用的,下面有一个例子,会阐述为什么这样用
  3. 代码
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

  1. 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 + "继续执行部署操作");
        }
    }
}
  1. 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();
        }
    }

}
  1. 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.线程同步机制

  1. 内部锁和显式锁
    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);
        }
    }
}
  1. CAS(Compare And Set)
  2. volidate
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值