最后
毕竟工作也这么久了 ,除了途虎一轮,也七七八八面试了不少大厂,像阿里、饿了么、美团、滴滴这些面试过程就不一一写在这篇文章上了。我会整理一份详细的面试过程及大家想知道的一些问题细节
美团面试经验
字节面试经验
菜鸟面试经验
蚂蚁金服面试经验
唯品会面试经验
因篇幅有限,图文无法详细发出
/**
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,
当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。
一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,
就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,
然后会返回结果或者抛出异常。
只计算一次
get方法放到最后
*/
线程间通信
线程间通信:1、生产者+消费者2、通知等待唤醒机制
synchronized实现
代码:
package com.atguigu.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.omg.IOP.Codec;
class ShareDataOne//资源类
{
private int number = 0;//初始值为零的一个变量
public synchronized void increment() throws InterruptedException
{
//1判断
if(number !=0 ) {
this.wait();
}
//2干活
++number;
System.out.println(Thread.currentThread().getName()+“\t”+number);
//3通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException
{
// 1判断
if (number == 0) {
this.wait();
}
// 2干活
–number;
System.out.println(Thread.currentThread().getName() + “\t” + number);
// 3通知
this.notifyAll();
}
}
/**
-
@Description:
*现在两个线程,
-
可以操作初始值为零的一个变量,
-
实现一个线程对该变量加1,一个线程对该变量减1,
-
交替,来10轮。
-
@author xialei
-
- 笔记:Java里面如何进行工程级别的多线程编写
-
1 多线程变成模板(套路)-----上
-
1.1 线程 操作 资源类
-
1.2 高内聚 低耦合
-
2 多线程变成模板(套路)-----下
-
2.1 判断
-
2.2 干活
-
2.3 通知
*/
public class NotifyWaitDemoOne
{
public static void main(String[] args)
{
ShareDataOne sd = new ShareDataOne();
new Thread(() -> {
for (int i = 1; i < 10; i++) {
try {
sd.increment();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, “A”).start();
new Thread(() -> {
for (int i = 1; i < 10; i++) {
try {
sd.decrement();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, “B”).start();
}
}
/*
-
-
2 多线程变成模板(套路)-----下
-
2.1 判断
-
2.2 干活
-
2.3 通知
-
3 防止虚假唤醒用while
-
*/
换成4个线程
换成4个线程会导致错误,虚假唤醒
原因:在java多线程判断时,不能用if,程序出事出在了判断上面,
突然有一天加的线程进到if了,突然中断了交出控制权,
没有进行验证,而是直接走下去了,加了两次,甚至多次
解决方法:
解决虚假唤醒:查看API,java.lang.Object
中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while
代码:
package com.atguigu.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.omg.IOP.Codec;
class ShareData//资源类
{
private int number = 0;//初始值为零的一个变量
public synchronized void increment() throws InterruptedException
{
//判断
while(number!=0) {
this.wait();
}
//干活
++number;
System.out.println(Thread.currentThread().getName()+" \t "+number);
//通知
this.notifyAll();;
}
public synchronized void decrement() throws InterruptedException
{
//判断
while(number!=1) {
this.wait();
}
//干活
–number;
System.out.println(Thread.currentThread().getName()+" \t "+number);
//通知
this.notifyAll();
}
}
/**
-
@Description:
*现在两个线程,
-
可以操作初始值为零的一个变量,
-
实现一个线程对该变量加1,一个线程对该变量减1,
-
交替,来10轮。
-
- 笔记:Java里面如何进行工程级别的多线程编写
-
1 多线程变成模板(套路)-----上
-
1.1 线程 操作 资源类
-
1.2 高内聚 低耦合
-
2 多线程变成模板(套路)-----下
-
2.1 判断
-
2.2 干活
-
2.3 通知
*/
public class NotifyWaitDemo
{
public static void main(String[] args)
{
ShareData sd = new ShareData();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “A”).start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “B”).start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “C”).start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “D”).start();
}
}
/*
-
-
2 多线程变成模板(套路)-----下
-
2.1 判断
-
2.2 干活
-
2.3 通知
-
3 防止虚假唤醒用while
-
*/
原理图:
java8新版实现
对标实现
Condition
Condition:查看API,java.util.concurrent
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
代码:
package com.atguigu.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.omg.IOP.Codec;
class ShareData//资源类
{
private int number = 0;//初始值为零的一个变量
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException
{
lock.lock();
try {
//判断
while(number!=0) {
condition.await();
}
//干活
++number;
System.out.println(Thread.currentThread().getName()+" \t "+number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException
{
lock.lock();
try {
//判断
while(number!=1) {
condition.await();
}
//干活
–number;
System.out.println(Thread.currentThread().getName()+" \t "+number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/*public synchronized void increment() throws InterruptedException
{
//判断
while(number!=0) {
this.wait();
}
//干活
++number;
System.out.println(Thread.currentThread().getName()+" \t "+number);
//通知
this.notifyAll();;
}
public synchronized void decrement() throws InterruptedException
{
//判断
while(number!=1) {
this.wait();
}
//干活
–number;
System.out.println(Thread.currentThread().getName()+" \t "+number);
//通知
this.notifyAll();
}*/
}
/**
- @Description:
*现在两个线程,
-
可以操作初始值为零的一个变量,
-
实现一个线程对该变量加1,一个线程对该变量减1,
-
交替,来10轮。
-
@author xialei
-
- 笔记:Java里面如何进行工程级别的多线程编写
-
1 多线程变成模板(套路)-----上
-
1.1 线程 操作 资源类
-
1.2 高内聚 低耦合
-
2 多线程变成模板(套路)-----下
-
2.1 判断
-
2.2 干活
-
2.3 通知
*/
public class NotifyWaitDemo
{
public static void main(String[] args)
{
ShareData sd = new ShareData();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “A”).start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “B”).start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “C”).start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
sd.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, “D”).start();
}
}
/*
-
-
2 多线程变成模板(套路)-----下
-
2.1 判断
-
2.2 干活
-
2.3 通知
-
3 防止虚假唤醒用while
-
*/
线程间定制化调用通信
线程-调用-资源类
判断-干活-通知
1、有顺序通知,需要有标识位
2、有一个锁Lock,3把钥匙Condition
3、判断标志位
4、输出线程名+第几次+第几轮
5、修改标志位,通知下一个
代码:
package com.atguigu.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareResource
{
private int number = 1;//1:A 2:B 3:C
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print5(int totalLoopNumber)
{
lock.lock();
try
{
//1 判断
while(number != 1)
{
//A 就要停止
c1.await();
}
//2 干活
for (int i = 1; i <=5; i++)
{
System.out.println(Thread.currentThread().getName()+“\t”+i+"\t totalLoopNumber: "+totalLoopNumber);
}
//3 通知
number = 2;
c2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(int totalLoopNumber)
{
lock.lock();
try
{
//1 判断
while(number != 2)
{
//A 就要停止
c2.await();
}
//2 干活
for (int i = 1; i <=10; i++)
{
System.out.println(Thread.currentThread().getName()+“\t”+i+"\t totalLoopNumber: "+totalLoopNumber);
}
//3 通知
number = 3;
c3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15(int totalLoopNumber)
{
lock.lock();
try
{
//1 判断
while(number != 3)
{
//A 就要停止
c3.await();
}
//2 干活
for (int i = 1; i <=15; i++)
{
System.out.println(Thread.currentThread().getName()+“\t”+i+"\t totalLoopNumber: "+totalLoopNumber);
}
//3 通知
number = 1;
c1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
-
@Description:
-
多线程之间按顺序调用,实现A->B->C
-
三个线程启动,要求如下:
-
AA打印5次,BB打印10次,CC打印15次
-
接着
-
AA打印5次,BB打印10次,CC打印15次
-
…来10轮
-
@author xialei
*/
public class ThreadOrderAccess
{
public static void main(String[] args)
{
ShareResource sr = new ShareResource();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
sr.print5(i);
}
}, “AA”).start();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
sr.print10(i);
}
}, “BB”).start();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
sr.print15(i);
}
}, “CC”).start();
}
}
多线程锁
锁的8个问题
1 标准访问,先打印短信还是邮件
2 停4秒在短信方法内,先打印短信还是邮件
3 普通的hello方法,是先打短信还是hello
4 现在有两部手机,先打印短信还是邮件
5 两个静态同步方法,1部手机,先打印短信还是邮件
6 两个静态同步方法,2部手机,先打印短信还是邮件
7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
运行答案:
1、短信
2、短信
3、Hello
4、邮件
5、短信
6、短信
7、邮件
8、邮件
锁的分析:
A 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
而不管是同一个实例对象的静态同步方法之间,
还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
代码:
package com.atguigu.thread;
import java.util.concurrent.TimeUnit;
class Phone
{
public synchronized void sendSMS() throws Exception
{
System.out.println(“------sendSMS”);
}
public synchronized void sendEmail() throws Exception
{
System.out.println(“------sendEmail”);
}
public void getHello()
{
System.out.println(“------getHello”);
}
}
/**
-
@Description: 8锁
-
@author xialei
1 标准访问,先打印短信还是邮件
2 停4秒在短信方法内,先打印短信还是邮件
3 新增普通的hello方法,是先打短信还是hello
4 现在有两部手机,先打印短信还是邮件
5 两个静态同步方法,1部手机,先打印短信还是邮件
6 两个静态同步方法,2部手机,先打印短信还是邮件
7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
*/
public class Lock_8
{
public static void main(String[] args) throws Exception
{
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, “AA”).start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendEmail();
//phone.getHello();
//phone2.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, “BB”).start();
}
}
JUC强大的辅助类讲解
ReentrantReadWriteLock读写锁
package com.atguigu.thread;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyQueue
{
private Object obj;
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void readObj()
{
rwLock.readLock().lock();
try
{
System.out.println(Thread.currentThread().getName()+“\t”+obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
public void writeObj(Object obj)
{
rwLock.writeLock().lock();
try
{
this.obj = obj;
System.out.println(Thread.currentThread().getName()+“writeThread:\t”+obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
}
/**
-
@Description: 一个线程写入,100个线程读取
-
@author xialei
*/
public class ReadWriteLockDemo
{
public static void main(String[] args) throws InterruptedException
{
MyQueue q = new MyQueue();
new Thread(() -> {
q.writeObj(“ClassName1221”);
}, “AAAAA”).start();
for (int i = 1; i <=100; i++)
{
new Thread(() -> {
q.readObj();
},String.valueOf(i)).start();
}
}
}
CountDownLatch减少计数
原理:
* CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。
* 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
* 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
代码:
package com.atguigu.thread;
import java.util.concurrent.CountDownLatch;
/**
-
@Description:
-
*让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒。
-
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。
-
其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
-
当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
-
解释:6个同学陆续离开教室后值班同学才可以关门。
-
main主线程必须要等前面6个线程完成全部工作后,自己才能开干
*/
public class CountDownLatchDemo
{
public static void main(String[] args) throws InterruptedException
{
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <=6; i++) //6个上自习的同学,各自离开教室的时间不一致
{
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+“\t 号同学离开教室”);
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+“\t****** 班长关门走人,main线程是班长”);
}
}
CyclicBarrier循环栅栏
原理:
-
CyclicBarrier
-
的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,
-
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,
-
直到最后一个线程到达屏障时,屏障才会开门,所有
-
被屏障拦截的线程才会继续干活。
-
线程进入屏障通过CyclicBarrier的await()方法。
代码:
独家面经总结,超级精彩
本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!
Java面试准备
准确的说这里又分为两部分:
- Java刷题
- 算法刷题
Java刷题:此份文档详细记录了千道面试题与详解;
t方法阻塞的线程会被唤醒,继续执行。
代码:
package com.atguigu.thread;
import java.util.concurrent.CountDownLatch;
/**
-
@Description:
-
*让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒。
-
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。
-
其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
-
当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
-
解释:6个同学陆续离开教室后值班同学才可以关门。
-
main主线程必须要等前面6个线程完成全部工作后,自己才能开干
*/
public class CountDownLatchDemo
{
public static void main(String[] args) throws InterruptedException
{
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <=6; i++) //6个上自习的同学,各自离开教室的时间不一致
{
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+“\t 号同学离开教室”);
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+“\t****** 班长关门走人,main线程是班长”);
}
}
CyclicBarrier循环栅栏
原理:
-
CyclicBarrier
-
的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,
-
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,
-
直到最后一个线程到达屏障时,屏障才会开门,所有
-
被屏障拦截的线程才会继续干活。
-
线程进入屏障通过CyclicBarrier的await()方法。
代码:
独家面经总结,超级精彩
本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!
[外链图片转存中…(img-0MaWiLCa-1715434542352)]
[外链图片转存中…(img-ZcygbSXc-1715434542352)]
[外链图片转存中…(img-vmd4Mbf5-1715434542352)]
[外链图片转存中…(img-9h6eAWYP-1715434542353)]
Java面试准备
准确的说这里又分为两部分:
- Java刷题
- 算法刷题
Java刷题:此份文档详细记录了千道面试题与详解;
[外链图片转存中…(img-AiIUvycT-1715434542353)]
[外链图片转存中…(img-SOvRmQlx-1715434542353)]