Java多线程入门以及相关面试题

实现多线程的几种方式

1.继承Thread类,重写run()方法;
2.实现Runnable接口,重写run()方法;
3.实现Callable接口,重写call()方法,call()可以有返回值,并且可以抛出异常,以上两种都不行。
在实际开发中,以上三种方法都比较少用,通常是使用线程池来创建线程,在阿里巴巴开发手册中,强制线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 Executors底层也是通过new ThreadPoolExecutor创建线程池的。

为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法

我们new一个Thread,线程就进入新建状态,调用start()方法,线程启动进入就绪状态,当分配到时间片后就可以开始运行了。start()会执行线程相应的准备工作,然后自动执行run()方法。这是真正的多线程工作。
如果我们直接调用run()方法,run方法会被当成main线程下一个普通方法的执行,并不会在某个线程中执行它,不是多线程工作。

使用线程池的好处

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程的几种状态

在这里插入图片描述
在这里插入图片描述由上图可以看出:线程创建之后它将处于 NEW(新建) 状态,调用 start() 方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 RUNNING(运行) 状态。
当线程执行 wait()方法之后,线程进入 WAITING(等待) 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)方法或 wait(long millis)方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。

sleep()和wait()的区别

1.sleep()方法在Thread类中,wait()方法在Object中,任何类都有wait()方法。
2.sleep()不会释放同步锁,在线程slee的时间内其它线程不能访问该线程正在执行的资源;wait()会释放同步锁。
3.sleep()通常用来暂停线程的执行;wait()通常用来线程通信。
4.sleep()方法被调用后线程会自动苏醒,而wait()不会,需要其它线程调用notify()方法或者notifyAll()方法唤醒它,或者可以使用 wait(long timeout)超时后线程会自动苏醒。

死锁的四个条件

1.互斥条件:该资源任意一个时刻只由一个线程占用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

如何避免死锁

我上面说了产生死锁的四个必要条件,为了避免死锁,我们只要破坏产生死锁的四个条件中的其中一个就可以了。现在我们来挨个分析一下:
1.破坏互斥条件 :这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
2.破坏请求与保持条件 :一次性申请所有的资源。
3.破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
4.破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

synchronized和lock的区别

1.synchronized是Java的一个关键字,lock是Java的一个接口。
2.synchronized不需要手动释放锁,而lock需要手动释放锁,如果不释放锁会造成死锁。
3.synchronized无法判断获取锁的状态,lock可以判断是否获取到了锁。
4.synchronized线程1(获得锁,阻塞了),线程2(一直等待线程1释放锁),lock锁不一定会一直等待下去。
3.synchronized只能是非公平锁,而lock可以指定是非公平锁或者公平所。
公平锁,顾名思义,先等待的线程先获得锁,而非公平锁则不一定。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。

并发编程的三个重要特性

1.原子性 : 一个的操作或者多次操作,要么所有的操作全部都得到执行并且不会收到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。synchronized 可以保证代码片段的原子性。
2.可见性 :当一个变量对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。volatile 关键字可以保证共享变量的可见性。
3.有序性 :代码在执行的过程中的先后顺序,Java 在编译器以及运行期间的优化,代码的执行顺序未必就是编写代码时候的顺序。volatile 关键字可以禁止指令进行重排序优化。

悲观锁和乐观锁

悲观锁:总是认为是最坏的情况,每次去取数据总认为别人会修改,因此每次在拿数据的时候都会上锁。
乐观锁:总是认为是最好的情况,每次去取数据总认为别人不会修改,所以不会上锁,但是在更新数据的时候会判断一下再此期间别人有没有更新这个数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值