目录名称
字节面试题1:
使用锁实现两个线程打印1~100,其中第一个线程打印奇数,第二个而线程打印偶数。
解法1:使用Object的wait、notify和notifyAll方法
public class WaitAndNotify {
public static void main(String[] args) throws InterruptedException {
PrintNumber number = new PrintNumber();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Thread(number));
exec.execute(new Thread(number));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}
}
class PrintNumber implements Runnable{
private int counter = 1;
@Override
public void run() {
synchronized (this){//this是PrintNumber实例化对象
while(counter <= 100){
try {
System.out.println(Thread.currentThread().getName()+","+counter);
counter++;
notify();//通知其他阻塞线程变成同步等待状态
wait();//阻塞当前线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
方法2:使用lock和condition
contion使用await()、singal、signalAll依次代替了Object中的wait、notify、notifyAll字段,要求condition的三个方法必须在lock()和unlock()包围。await方法也是首先释放对象锁,然后将当前线程阻塞,放入到
class LockConditionPrintNumber {
public static void main(String[] args) {
PrintNumber2 number2 = new PrintNumber2();
new Thread(number2,"线程A").start();
new Thread(number2,"线程B").start();
}
static class PrintNumber2 implements Runnable{
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
private int counter =1;
@Override
public void run() {
try{
lock.lock();
while(counter <= 100){
System.out.println(Thread.currentThread().getName()+","+counter++);
condition.signal();
condition.await();
}
}catch (Exception e){
}finally {
lock.unlock();
}
}
}
}
方法3:使用volatile关键字
public class VolatilePrintNumber {
public static void main(String[] args) {
PrintNumber1 number1 = new PrintNumber1();
new Thread(()->{//打印1~100之间50个奇数
for(int i = 0;i < 50;i++){
number1.printOdd();
}
},"线程A").start();
new Thread(()->{//打印1~100之间50个偶数
for(int i = 0;i < 50;i++){
number1.printEven();
}
},"线程B").start();
}
}
class PrintNumber1{
private volatile int flag = 1;
private int counter = 1;
public void printOdd(){
while(flag!=1){//不能使用if
Thread.yield();//让出CPU控制权
}
System.out.println(Thread.currentThread().getName()+","+counter++);
flag= 2;
}
public void printEven(){
while(flag!=2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+","+counter++);
flag = 1;
}
}
Thread.yield是交出当前CPU的控制权,但无法控制下次CPU分配给谁,会出现空循环等待情况。
方法4:使用Semphore
public class SemphorePrintNumber {
public static void main(String[] args) {
Semaphore s1 = new Semaphore(1);
Semaphore s2 = new Semaphore(0);
PrintNumber4 number4 = new PrintNumber4();
new Thread(()->{
try {
number4.printNumber(s1,s2);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"线程A").start();
new Thread(()->{
try {
number4.printNumber(s2,s1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"线程B").start();
}
}
class PrintNumber4{
private int counter =1;
public void printNumber(Semaphore s1,Semaphore s2) throws InterruptedException {
while(counter < 100){
s1.acquire();
System.out.println(Thread.currentThread().getName()+","+counter++);
s2.release();
}
}
}
小结:基于加锁的方式,实现两个线程通过通信协作的方式交替打印,基于volatile的方式,虽然实现了两个线程交替打印数字,但每个线程执行完成之后,只是主动让出了线程,并没有通知另外一个线程执行,会出现很多无效的线程上限文之间切换和循环判断,严格来说,不属于线程之间协作,效率也比较低下。
错误解法:基于while循环
public class WhilePrintNumber {
public static void main(String[] args) {
int counter = 1;
PrintNumber3 number3 = new PrintNumber3();
new Thread(()->{
number3.printOdd();
},"线程A").start();
new Thread(()->{
number3.printEven();
},"线程B").start();
}
}
class PrintNumber3{
private int counter = 1;
public void printOdd(){
while(counter<=1000){
if(counter%2!=0){
System.out.println(Thread.currentThread().getName()+","+counter);
counter++;
}
}
}
public void printEven(){
while(counter<=1000){
if(counter%2==0){
System.out.println(Thread.currentThread().getName()+","+counter);
counter++;
}
}
}
}
自己猜测: 每次程序有可能结束,有可能不能成功结束。截图情况不能继续执行原因,在打印完303时,counter++还没有执行,就切换到线程B,线程B在执行while循环条件303%2=1,显然,不成立,一直在执行while循环判断,线程A一值得不到CPU的控制权,counter的值一值无法改变。
字节面试题2:
实现缓存替换,可以选择通过配置实现LFU和LRU方式。
力扣460:https://leetcode-cn.com/problems/lfu-cache/
Lazada面试题2:
public MyMap implements Map{
public void put(String key);
public void remove(String key);
}
自己思考主要让描述hashMap的一个插入过程和是否阅读过源码,写出大体整个过程就行。
携程面试题1:
使用多线程实现一个生产者和消费者。 该模型实现了生产者和消费者之间的解耦、异步和平衡生产者和消费者之间的速度,队列相当于存储产品的缓冲区。
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Random;
public class ProducerAndConsumer {
public static void main(String[] args) {
Queue<Product> queue = new ArrayDeque<>();
int maxCapacity = 10;//容器大小为10
for(int i = 0;i < 3;i++){//创建3个生产者和3个消费者,
new Thread(new Producer(queue,maxCapacity),""+i).start();
new Thread(new Consumer(queue,maxCapacity),""+i).start();
}
}
}
class Producer implements Runnable{
private Queue<Product> container;
private int maxCapacity;
public Producer(Queue queue,int maxCapacity){
this.container = queue;
this.maxCapacity = maxCapacity;
}
@Override
public void run() {
while(true){
synchronized (container){//保证在一个时间只能一个线程访问容器
Random random = new Random();
Product newProduct = new Product(String.valueOf(random.nextInt()));
while(container.size()==maxCapacity){
System.out.println("容器已经满了,生成的产品放不下了");
container.notifyAll();//通知消费者去消费
try {
container.wait();//当前线程先阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(200);//表示生产产品所需时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者:"+Thread.currentThread().getName()+"生产了一个产品:"+newProduct.toString());
container.offer(newProduct);
container.notifyAll();//新生产了产品,通知其她消费者去消费
}
}
}
}
class Consumer implements Runnable{
private Queue<Product> container;
private int maxCapacity;
public Consumer(Queue queue,int maxCapacity){
this.container = queue;
this.maxCapacity = maxCapacity;
}
@Override
public void run() {
while(true){
synchronized (container){//保证在一个时间只能一个线程访问容器
while(container.size()==0){
System.out.println(Thread.currentThread().getName()+"容器内没有产品,无法消费,等待生产品..");
container.notifyAll();//通知消费者去消费
try {
container.wait();//当前线程先阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Product newProduct = container.poll();
try {
Thread.sleep(200);//表示消耗产品所需时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者:"+Thread.currentThread().getName()+"消费了一个产品:"+newProduct.toString());
container.notifyAll();//有空间可以用来盛产品,通知其生产者去生产
}
}
}
}
class Product{
private String name;
public Product(){}
public Product(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
'}';
}
}
注意点:在使用Object的wait/notify/notifyAll,需要对对象加锁和释放锁,所以要想直接使用wait/notify/notifyAll,则使用synchronized(this),this表示当前类的对象,当对synchronized(container)加锁,要使用container.wait/container.notify/container.notifyAll。
携程面试题2:
求能被3整除的最大64位整数,要求该被数只能由1组成
public static void main(String[] args) {
long cur = 1,res = 0;//long占8个字节,64位
long jinwei = 1;
while(cur>0){//curr溢出将变成负数
jinwei = jinwei*10;
if(cur%3==0){//保存当前最大能被3整除的数
res = cur;
}
cur = cur+jinwei;
}
System.out.println(res);
System.out.println(cur);
}
携程面试题3:
使用一下三个函数实现生成1~100之间任意随机数:
func2();//生成1~2之间的随机数
func3();//生成1~3之间的随机数
func5();// 生成1~ 5之间的随机数首先使用者三个函数生成一个1~ 10之间的函数。
思路:
func100=func10+(func10-1)*10 ---公式(1)
func10-1可以范围在1~ 9,(func10-1)*10范围在【0,10,20,30,40,50,60,70,80,90】,func10+(func10-1)*10范围在1~100,并且每个数生成概率相等0.01。
同理:func10 = func5+(func2-1)*2;---公式(2)
将公式(2)带入公式(1),可得:
func100=func10+(func10-1)*10
= func5+(func2-1)*2+(func5+(func2-1)*2-1)*10
=11func5+22func2-32 公式(3)