美文网首页小卜java
JAVA面试汇总(二)多线程(二)

JAVA面试汇总(二)多线程(二)

作者: 汤太咸啊 | 来源:发表于2021-11-30 18:00 被阅读0次

多线程内容比较多,今天写完了第二篇,后边应该还有三或者四。

1. 同步方法和同步块,哪个是更好的选择?

(1)同步方法:即有synchronized关键字修饰的方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。
(2)同步块:即有synchronized关键字修饰的语句块。 被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。
(3)同步是一种高开销的操作,因此应该尽量减少同步的内容。 通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

2. run()和start()方法区别

(1)run()是实现Runable接口和继承Thread必须重写的,多线程实现的主方法,写的是具体的多线程执行的编码内容。
(2)start()方法是启动执行多线程的方法,表示真正开始运行多线程方法。

3. 如何控制某个方法允许并发访问线程的个数?

Semaphore创建时设定最多同时访问个数,acquire()申请新请求计数+1,达到设定数值后,内部会执行Thread的挂起操作,并把超过指定个数的线程放入到队列中等待唤醒。当release()后会唤醒队列里的前几个线程执行。

import java.util.concurrent.Semaphore;
public class SemaphoreTest {
    static Semaphore semaphore = new Semaphore(2, true);
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    test();
                }
            }).start();
        }
    }
    public static void test() {
        try {
            //这块可以运行时可以看到实际时先打印出来,说明已经进入了方法了
            System.out.println("waiting"+Thread.currentThread().getName());
            //申请一个新的请求
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"进来了");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"走了");
        //释放一个请求
        semaphore.release();
    }
}

4. 在Java中wait和seelp方法的不同;

(1)wait()方法,是属于Object类,wait()出发后,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态
(2)sleep()方法,是属于Thread类的,sleep()方法的执行过程中,线程不会释放对象锁

5. Thread类中的yield方法有什么作用?

Thread.yield()可以暂停当前正在执行的线程对象,让其他有相同优先级的线程执行。它是一个静态方法而且只保证当前线程放弃CPU占用而不能保证其它线程一定能占用CPU,执行yield()的线程有可能在进入到暂停状态后马上又被执行。

6. 什么是不可变对象,它对写并发应用有什么帮助?

对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变, 例如String、基本类型的包装类(Long,Double等)、BigInteger和BigDecimal等。主要是线程安全,在多线程情况下不可改变,天生线程安全。

7. 谈谈wait/notify关键字的理解

都是继承自Object的方法,wait()方法用来将当前线程置入休眠状态,notify()用来通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑选出其中一个wait()状态的线程来发出通知,并使它等待获取该对象的对象锁。

8. 为什么wait, notify 和 notifyAll这些方法不在thread类里面?

简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中,因为锁属于对象。
那么复杂的说呢?因为锁是在对象头中的markWorld的中标记的,wait和notify直接理解为get和set方法,实际上就是对锁的操作,因此理解来说,这个应该放到Object类中,而不是在Thread类中。

9. 什么导致线程阻塞?

(1)Thread.sleep方法休眠一段时间,线程放弃cpu,一段时间后在恢复运行
(2)线程执行wait()方法,进入阻塞状态,直到notify或者notifyAll方法唤醒
(3)等待相关资源:线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态。例如线程执行System.in.read()时,如果用户没有输入,则一直等待
(4)调用了yield方法后,同样会进入阻塞状态。
(5)suspend() 是让线程进入阻塞状态,没有resume()是不会恢复的。
(6)join()方法,被join的Thread要等待join的Thread执行完毕才能继续执行。

10. 讲一下java中的同步的方法

(1)当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,不被其他线程的调用,从而保证了该变量的唯一性和准确性。
(2)同步方法有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
(3)synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。
(4)synchronized代码块同步关键代码
(5)使用特殊域变量(volatile)实现线程同步,但是不能完全避免同步问题,因为其不能保证原子操作,它只是在取得该变量值的时候是从内存中读取的而不是存缓存中读取。
(6)使用重入锁实现线程同步,通过lock和unlock来控制方法的执行锁。
(7)ThreadLocal的方式,由于每个线程都是独立的ThreadLocal,所以其实并不存在什么同步,以及冲突,每个线程都在自己的空间内执行。

public class Bank {
    //实际上这个是每个线程独立的ThreadLocal,并不共享,因此多个线程调用addMoney
    //或者subMoney的时候,都是在自己的独立线程内的,并不共享,不存在冲突
    private static ThreadLocal<Integer> count = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
    // 存钱
    public void addMoney(int money) {
        count.set(count.get() + money);
        System.out.println(System.currentTimeMillis() + "存进:" + money);

    }
    //取钱
    public void subMoney(int money) {
        if (count.get() - money < 0) {
            System.out.println("余额不足");
            return;
        }
        count.set(count.get() - money);
        System.out.println(+System.currentTimeMillis() + "取出:" + money);
    }
    //查询
    public void lookMoney() {
        System.out.println("账户余额:" + count.get());
    }
}

相关文章

网友评论

    本文标题:JAVA面试汇总(二)多线程(二)

    本文链接:https://www.haomeiwen.com/subject/hdidxrtx.html