多线程面试

多线程

并行和并发的区别

  • 并行:多个处理器处理多个任务
  • 并发:一个处理器处理多个任务,多线程会引起并发问题。(多个线程操作同一资源)

线程和进程的区别

  • 进程:资源分配的最小单位
  • 线程:一个进程可以包含多个线程,CPU调度的最小单位
  • 联系:
  • 一个程序至少有一个进程,一个进程至少有一个线程
  • 即使不创建线程,也会存在其他的线程,比如main,gc
  • 在一个进程中,不同线程之间可以实现数据共享

创建线程有哪几种方式

  • 继承Thread类,重写run方法,创建该类的对象,调用start()方法开启线程。
  • 实现Runnable接口,重写run方法,创建该类对象,new Thread(对象).start( )开启线程。
  • 实现Callable接口,重写call方法,创建执行服务,提交执行,获取结果,关闭服务。

Runnable和Callable接口的区别

  • Runnable接口:run方法无返回值
  • Callable接口:call方法有返回值,可以抛出异常。

线程有哪几种状态

  • 创建,生成线程对象,但是没有调用start方法。
  • 就绪,调用start方法,调度器还没有调度该线程
  • 运行,调度器调度了该线程,执行run方法体。
  • 阻塞,等待某项资源加载完成后再运行,sleep,wait方法都致阻塞
  • 死亡,run方法执行结束或调用stop方法(不建议)

怎么停止线程

public class Demo3Test implements Runnable{
   //定义一个标志
   private boolean flag = true; 
   @Override
   public void run() {
       while (flag){
           //线程体
       }
   } 
   //对外提供一个方法,调用该方法时,线程会自动停止
   public void stop(){
       this.flag = false;
   }
}

怎么解决并发问题

  • 使用线程同步,就是一种等待机制,多个需要访问该资源的线程进入对象的等待池形成队列
  • 线程同步(synchronized)形成条件:队列+锁
  • synchronized修饰符默认锁的是类本身对象,如果操作的是其他类的对象,需要使用同步代码块
  • synchronized(需要增删改的对象){ 方法体 };
  • Lock:ReentrantLock类,lock()开启锁,unlock()关闭锁。

使用线程同步后存在的问题

  • 一个线程获取锁,在它释放锁之前,其他线程都必须等待
  • 频繁的加锁,释放锁会导致性能问题
  • 如果一个优先级低的线程优先获取了锁,会存在性能倒置问题。

什么是死锁

  • 某一个同步块同时拥有两个以上对象的锁时。

synchronized和Lock的区别

  • synchronized:隐式锁(出了作用域自动释放)
  • Lock:lock是显式锁(手动开启和关闭锁),JVM花费较少的时间调度线程,性能较好

优先级:Lock>同步代码块>同步方法修饰

Lock锁使用

public class Synch implements Runnable {
    private int ticket=200;
    private final ReentrantLock lock = new ReentrantLock();

    public void run() {
         buy();   
    }

    private void buy(){
        try{
            //开启锁
            lock.lock();
            if (ticket<=0){
                flag=false;
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到"+ticket--);
        }finally {
            //关闭锁
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        Synch synch = new Synch();
        //为线程命名
        new Thread(synch,"aaa").start();
        new Thread(synch,"bbbb").start();
        new Thread(synch,"cccc").start();
        new Thread(synch,"dddd").start();
        new Thread(synch,"eeee").start();
    }
}

线程池

  • 经常创建和销毁线程,对性能影响较大

  • 提前创建好线程放入池子,用完放回池子,可以继续使用。

public static void main(String[] args) {
    //实现Runnable接口的类
    Synch synch = new Synch();
    //开启一个5个线程的线程池
    ExecutorService service = Executors.newFixedThreadPool(5);

    service.execute(new Thread(synch));
    service.execute(new Thread(synch));
    service.execute(new Thread(synch));
    service.execute(new Thread(synch));
    service.shutdown();

}

线程池中 submit()和 execute()方法有什么区别?

  • 接收的参数不一样
  • submit有返回值,而execute没有
  • submit方便Exception处理

synchronized关键字可以作用范围

synchronized

  • 是一种同步锁,效率较低。一个线程调用时,其余线程只能干等着。

  • 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

  • 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

  • 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

  • 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

  • 如果synchronized加在一个类的普通方法上,那么相当于synchronized(this)。

  • 如果synchronized加载一个类的静态方法上,那么相当于synchronized(Class对象)。

  • Synchronized修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”。

  • Synchronized修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。

如何保证成员变量线程安全

  • 为该类中每个操作该成员变量的方法都加上synchronized关键字。
  • 那么相当于为当前对象加锁。(this)。

ThreadLocal

当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。

posted @ 2022-03-11 14:37  初夏那片海  阅读(38)  评论(0)    收藏  举报