java基础面试题系列(91-100)

20200717 by 1z

91、举例说明同步和异步

同步: 发送消息之后需要等待反馈,然后再开始下一次发送信息。
异步: 发送一个请求,不等待返回,随时可以再次发送下一个请求。

同步和异步的区别: 同步需要等待,异步不需要等待。

例子;
电话是一个同步的例子,需要等待接收者接通电话才可以进行下一步行为
广播是一个异步的例子,不需要等待接收者的反馈信息,可以连续发送信息
  1. 请介绍一下线程同步 和 线程调度的相关方法
(1)wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;

(2)sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; //sleep(int times)

(3)notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;[随机唤醒]

(4)notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

(5)通过Lock接口提供了显式的锁机制(explicit lock),增强了灵活性以及对线程的协调。Lock接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通信的Condition对象【Condition 可以实现精确唤醒(为每一个线程 分配一个condition 用来唤醒)】
此外,Java 5还提供了信号量机制(semaphore),信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。
  1. 当一个线程进入一个对象的synchronized方法A之后,其他线程是否可进入此对象的的synchronized的方法B?
不可以,当一个线程访问对象的synchronized方法后,该对象的对象锁已经被获取了,如果需要再次访问阻塞方法B的时候,需要等待释放
  1. 请简述一下线程的sleep() 方法 和 yield()方法有什么区别?
* sleep()方法给其他线程机会时不考虑线程的优先级,因此会给低优先级线程以运行的机会,yield()方法只会给相同优先级或者更高优先级以运行的机会

* 线程使用sleep()后转入阻塞状态,执行yield()方法后转入就绪状态(自己让出cpu之后,再次参与抢夺之中)

* sleep()可以设置延时的时间,yield()使用后,直接让出cpu资源

* sleep()声明抛出InterruptedException异常,而yield()方法没有任何声明异常

* sleep() 比 yield() 具有更好的可移植性
  1. 请回答以下几个问题:

95-1:java中有几种方法可以实现一个线程?

有三种方式可以实现一个线程
* 实现Runnable方法,简单,但是传参不方便
* 继承Thread重写run: 传递参数方便,但是java不支持多继承
* 使用FutureTask接口: 可以拿到任务的返回值 通过 实例化对象的get方法

coding
//1.继承Thread类 实现run方法
    public static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("I am a child thread by extending Thread");
        }
    }

    //2.使用Runnable接口实现线程创建
    public static class RunnableTask implements Runnable{

        @Override
        public void run() {
            System.out.println("I am a child thread by implementing Runnable ");
        }
    }

    //3.!!使用FutureTask
    public static class CallerTask implements Callable<String>{

        @Override
        public String call() throws Exception {
            System.out.println("I am a child thread by implementing Callable");
            //调用线程可以有返回值传递
            return "hello";
        }
    }
    public static class CallerTask2 implements Callable<String>{

        @Override
        public String call() throws Exception {
            System.out.println("I am a child thread2 by implementing Callable");
            return "CallerTask2Msg";
        }
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread thread=new MyThread();
        //直到调用start之后才算真正启动了线程,调用start后,线程进入了就绪态(已经获取了除CPU资源外的其他资源,获取CPU资源之后才会真正处于运行状态,在run结束后处于终止态)
        thread.start();
        System.out.println("----------------------------------------");
        RunnableTask task=new RunnableTask();
        new Thread(task).start();
        new Thread(task).start();
        FutureTask<String>futureTask=new FutureTask<>(new CallerTask());
        //在futureTask中有一个get方法 可以获取返回值
        System.out.println("------------------------------------------");
        new Thread(futureTask).start();
        //现在线程已经启动 开始进行返回值获取
        try {
            String result=futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        FutureTask<String> futureTask2=new FutureTask<String>(new CallerTask2());
        new Thread(futureTask2).start();
        //进行信息的反馈
        String result2 = futureTask2.get();
        System.out.println(result2);
    }
}

95-2:用什么关键词修饰同步方法?

synchronized

95-3:stop()和suspend()方法为什么不推荐使用,请说明原因?

 stop和suspend都有一些共同的点:都试图专横的控制一个给定了的线程的行为.
 1.stop这个方法将终止所有未结束的方法,包括run方法。当一个线程停止时候,他会立即释放所有他锁住对象上的锁。这会导致对象处于不一致的状态。(当需要去停止一个线程的时候,它无法知道何时调用stop是安全的)
 
 2.suspend不会破坏对象。但是,如果你用一个suspend挂起一个有锁的线程,那么在锁恢复之前将不会被释放。如果调用suspend的方法线程试图取得相同的锁,程序就会死锁。
  1. 请分别说明一下多线程和同步有几种实现方法,并且这些实现方法的具体内容都是什么?
95-1
有三种方式可以实现多线程
* 实现Runnable方法,简单,但是传参不方便
* 继承Thread重写run: 传递参数方便,但是java不支持多继承
* 使用FutureTask接口: 可以拿到任务的返回值 通过 实例化对象的get方法

同步五种方式
* 同步方法
* 同步代码块
* 使用volatile实现线程同步
* 使用重入锁实现线程同步
* 使用局部变量实现线程同步
-----------------------------------------------------------------------------------------
coding
    //1.继承Thread类 实现run方法
    public static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("I am a child thread by extending Thread");
        }
    }

    //2.使用Runnable接口实现线程创建
    public static class RunnableTask implements Runnable{

        @Override
        public void run() {
            System.out.println("I am a child thread by implementing Runnable ");
        }
    }

    //3.!!使用FutureTask
    public static class CallerTask implements Callable<String>{

        @Override
        public String call() throws Exception {
            System.out.println("I am a child thread by implementing Callable");
            //调用线程可以有返回值传递
            return "hello";
        }
    }
    public static class CallerTask2 implements Callable<String>{

        @Override
        public String call() throws Exception {
            System.out.println("I am a child thread2 by implementing Callable");
            return "CallerTask2Msg";
        }
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread thread=new MyThread();
        //直到调用start之后才算真正启动了线程,调用start后,线程进入了就绪态(已经获取了除CPU资源外的其他资源,获取CPU资源之后才会真正处于运行状态,在run结束后处于终止态)
        thread.start();
        System.out.println("----------------------------------------");
        RunnableTask task=new RunnableTask();
        new Thread(task).start();
        new Thread(task).start();
        FutureTask<String>futureTask=new FutureTask<>(new CallerTask());
        //在futureTask中有一个get方法 可以获取返回值
        System.out.println("------------------------------------------");
        new Thread(futureTask).start();
        //现在线程已经启动 开始进行返回值获取
        try {
            String result=futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        FutureTask<String> futureTask2=new FutureTask<String>(new CallerTask2());
        new Thread(futureTask2).start();
        //进行信息的反馈
        String result2 = futureTask2.get();
        System.out.println(result2);
    }
}
  1. 请说明你所知道的线程同步的方法
* 同步方法(synchronized 修饰方法)
* 同步代码块	(synchronized 修饰代码块)
* 使用volatile实现线程同步(使用volatile 修饰变量)
* 使用重入锁实现线程同步(ReentractLock)
* 使用局部变量实现线程同步(ThreadLocal)
  1. 启动一个线程是用run() 还是start()?
启动一个线程调用start(),使得线程处于就绪状态,之后可以被调度称为运行状态
run()是线程执行的代码
  1. 请使内部类实现线程设计四个线程,其中两个线程每次对j 增加1,其他两个线程对j减少1
public class ManyThread {
    // 采用 Runnable 接口方式创建的多条线程可以共享实例属性

    private int i;
    // 同步增加方法

    private synchronized void inc() {
        i++;
        System.out.println(Thread.currentThread().getName() + "--inc--" + i);
    }

    // 同步减算方法

    private synchronized void dec() {
        i--;
        System.out.println(Thread.currentThread().getName() + "--dec--" + i);
    }

    // 增加线程 注意是内部类,且是非静态的
    class Inc implements Runnable {
        public void run() {
            int i = 0;
            while (i++ < 100) {
                inc();
            }
        }
    }

    // 减算线程 注意是内部类,且是非静态的
    class Dec extends Thread {
        public void run() {
            int i = 0;
            while (i++ < 100) {
                dec();
            }
        }
    }

    public static void main(String[] args) {

        // 由于内部类是非静态的,所以这样需要Test的实例化才能调用生成内部类实例
        ManyThread t = new ManyThread();

        // 内部类的实例化
        Inc inc = t.new Inc(); //
        // Dec dec = t. new Dec();

        Thread thread = null;
        // 创建 2 个增加线程
        for (int i = 0; i < 2; i++) {
            thread = new Thread(inc); // 实现Runnable的类的实例化,使用带参数的Thread构造方法.
            thread.start();
        }

        // 创建 2 个减少线程
        for (int i = 0; i < 2; i++) {
            thread = t.new Dec(); // 继承Thread的类可以直接实例化.
            thread.start();
        }

    }

}

  1. 请说明线程中同步和异步有何异同?请举例说明什么情况下会使用到同步和异步?
同步: 发送消息之后需要等待反馈,然后再开始下一次发送信息。
异步: 发送一个请求,不等待返回,随时可以再次发送下一个请求。

同步可以避免脏数据的产生,假设是共享文件情境下,a和b都有着修改权限的能力。为了防止一个人读取到另外一个人删除的内容,采用同步的方式。进行按顺序操作。

异步可以提高效率,假设服务器主线程中需要启动一个子线程完成一些耗时操作,此时使用异步操作(客户端通过ajax发送异步请求),客户端无需等待直接运行,等到异步操作完成后反馈一条信息,客户端再进行回显即可。

你可能感兴趣的:(java基础面试题系列)