Java面试题总结

Java多线程(1)

2018-03-09  本文已影响29人  简祖明

开启线程的三种方式?

  1. 继承Thread类,重写run(),调用start;
  2. 实现Runnable接口,复写run(),将Runnable子类对象传递给Thread类对象,调用start;
  3. 创建FutureTask对象,创建Callable子类对象。重写call(相当于run)方法,将其传递给FutureTask对象(相当于一个Runnable)。创建Thread类对象,将FutureTask对象传递给Thread对象。调用start方法开启线程。这种方式可以获得线程执行完之后的返回值。

run()和start()方法区别

run()是一个普通方法,多线程中可以多次调用,start()开启一个线程。jvm内部机制规定在调用start()开启线程时,会自动调用run().

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

创建一个Semaphore对象,初始化访问线程的个数,在线程开始时通过acquire()请求信号,线程完成时release()释放信号即可控制访问线程的个数。

Java中wait和seelp方法的不同

sleep()属于Thread类中的,wait()属于Object。
sleep()睡眠时,保持对象锁,而wait()释放对象锁。wait()通过notify或notifyAll或指定时间来唤醒当前线程(需要获得锁对象),必须放在Snchronized block中,否则会抛出IllegalMonitorStateException

wait/notify关键字的理解

什么导致线程阻塞?

阻塞状态的特点是:该线程放弃CPU的使用,暂停运行,等待阻塞原因消除以后才能恢复运行。或者被其他线程中断退出阻塞状态,同时会抛出InterruptedException。
导致阻塞的原因有:

线程如何关闭?

  1. 使用stop()方法强行终止线程,不推荐,可能发生不可预料的结果;
  2. 使用退出标识,使线程正常退出;
public class ThreadSafe extends Thread {
    public volatile boolean exit = false; 
        public void run() { 
        while (!exit){
            //do something
        }
    } 
}

定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值.

  1. 使用interrupt方法中断线程。
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        /*
         * 在这里为一个循环,条件是判断线程的中断标志位是否中断
         */
        while (true&&(!Thread.currentThread().isInterrupted())) {
            try {
                Log.i("tag","线程运行中"+Thread.currentThread().getId());
                // 每执行一次暂停40毫秒
                //当sleep方法抛出InterruptedException  中断状态也会被清掉
                Thread.sleep(40);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //如果抛出异常则再次设置中断请求
                Thread.currentThread().interrupt();
            }
        }
    }
});
thread.start();
//触发条件设置中断
thread.interrupt();

java中同步的几种方式

  1. 同步方法
  2. 同步代码块
  3. 特殊域变量,volatile:
  1. 使用局部变量ThreadLocal实现
    使用局部变量实现线程同步,如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

注:ThreadLocal与同步机制

public class Bank{
    //使用ThreadLocal类管理共享变量account
    private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue(){
            return 100;
        }
    };
    public void save(int money){
        account.set(account.get()+money);
    }
    public int getAccount(){
        return account.get();
    }
}
  1. 使用重入锁ReentrantLock
class Bank {
    private int account = 100;
    //需要声明这个锁
    private Lock lock = new ReentrantLock();
    public int getAccount() {
        return account;
    }
    //这里不再需要synchronized 
    public void save(int money) {
        lock.lock();
        try{
            account += money;
        }finally{
            lock.unlock();
        }

    }
}
  1. 使用原子变量实现线程同步。如AtomicInteger,需要线程同步的根本原因就是因为变量的操作不是原子性的

如何保证线程安全?如何实现线程同步?

线程同步,同上。

两个进程同时要求写或者读,能不能实现?如何防止进程的同步?

  1. 允许多个读者同时执行读操作;
  2. 不允许读者、写者同时操作;
  3. 不允许多个写者同时操作。

java.util.concurrent.locks.ReadWriteLock;
java.util.concurrent.locks.ReentrantReadWriteLock;

控制访的数量即可。Java并发库的Semaphore可以完成信号量的控制,Semaphore可以控制某个资源可被同时访问的数量,通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可。

线程间操作List

1使用Collections.synchronizedList()构建List;2操作list的方法使用同步锁。
为何会两者都用,因为Collections.synchronizedList()构建的list只针对list的add(obj)、poll(obj)等方法做同步,在多线程中直接使用方法会同步,但是在操作list时,add(obj)、poll(obj)方法之前不能保证obj是否被其他线程操作过。

上一篇下一篇

猜你喜欢

热点阅读