十二、JavaSE-多线程和并发

  1. Java怎么保证多线程运行安全?

  • 线程安全是程序设计中的术语,指某个方法在多线程环境中被调用时,能正确的处理多个线程中的共享变量,是程序正确执行。

  • Java中线程安全体现在以下三个方面:

  1. 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作

  1. 可见性:一个线程对主内存的修改可以及时地被其他线程看到

  1. 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序

因此,只要满足上述三个条件,我们就可以说改代码是线程安全的。那么,Java中提供了如下解决方案:

  1. 使用sychronized关键字

  1. 使用线程安全类,如:java.util.concurrent包下的类

  1. 使用并发包下Lock相关锁

总结:想要代码满足线程安全,只需要代码满足原子性,可见性,有序性即可。

  1. 线程和进程的区别?Java实现的多线程的方式有哪几种?

线程和进程的区别:

  • 进程是程序的一次动态执行过程,每个进程都有自己的独立的内存空间。一个应用程序可以同时启动多个进程(比如浏览器可以开多个窗口,每个窗口就是一个进程)

  • 多进程操作系统能够运行多个进程,每个进程都能够循环利用所需要的CPU时间片,使得所有进程看上去像在同时运行一样。

  • 线程是进程的一个执行流程,一个进程可以由多个线程组成,也就是一个进程可以同时运行多个不同的线程,每个线程完成不同的任务。

  • 线程的并发运行:就是一个进程内若干个线程同时运行。(比如:word的拼写检查功能和首字母自动大写功能是word进程中的线程)

  • 线程和进程的关系是一个局部和整体的关系,每个进程都有操作系统分配独立的内存地址空间,而同一进程的多有线程都在同意地址空间工作。

  • 多线程的实现方式:Java多线程实现方式主要有四种:继承Thread类,实现Runnable接口,实现Callable接口通过FutureTask包装器来创建Thread线程,使用ExecutorService,Callable,Future实现有返回结果的多线程。

  1. 继承Thread类,重写run方法

  1. 实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target

  1. 通过Callable和FutureTask创建线程

  1. 通过线程池创建线程

  1. 线程有哪些基本状态,并描述每种状态?

  1. 同步和一部的区别?

  • 同步:发出一个调用之后,在没有得到结果之前,该调用就不可以返回,一直等待。

  • 异步:调用在发出之后,不用等待返回结果,该调用直接返回。

  1. 并发与并行的区别?

  • 并发:两个及两个以上的作业在同一时间段内执行。

  • 并行:两个及两个以上的作业在同一时刻执行。

最关键的点是:是否是同时执行。

  1. 线程的run()和start()有什么区别?

  • start:它的作用是启动一个新线程。通过start()方法启动线程,处于就绪(可运行)状态,一旦得到cpu时间片,就开始执行相应线程的run()方法,这个方法run()成为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。start()不能被重复调用。用start方法来启动线程,真正实现了多线程运行,即无需等待某个线程的run方法体代码执行完毕就直接继续执行下面的代码。这里无需等待run方法执行完毕,即可继续执行下面的代码,即进行了线程切换。

  • run():就和普通的成员方法一样,可以被重复调用。如果直接调用run方法,并不会启动新线程!程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可以继续执行下面的代码,这样就没有达到多线程的目的。

  • 总结:调用start()方法方可启动线程,而run()方法只是thread的一个普通方法调用,还是主线程里执行。

  1. 说一下runnable和callable有什么区别?

  • 最大的区别,runnable没有返回值,而现实callable接口的任务线程能返回执行结果

  • callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出。

  1. 什么是线程死锁

线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

如下图所示,线程A持有资源2,线程B持有资源1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

产生死锁必须具备以下四个条件:

  • 互斥条件:该资源任意一个时刻只有一个线程占用

  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

  • 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。

  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

  1. 如何避免线程死锁?

为了避免死锁,只要破坏产生死锁的四个条件中的其中一个就可以了。

  • 破坏互斥条件:这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)

  • 破坏请求与保持条件:一次性申请所有的资源

  • 破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

  • 破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏等待条件

  1. sleep()方法和wait()方法区别和共同点

  • 两者最主要的区别在于:sleep方法没有释放锁,而wait方法释放了锁

  • 两者都可以暂停线程的执行

  • wait通常被用于线程间交互/通信,sleep通常被用于暂停执行

  • wait()方法被停用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()方法。sleep()方法执行完成后,线程会自动苏醒,或者可以使用wait(long timeout)超时后线程会自动苏醒。

  1. 现在有线程T1、T2和T3,如何确保T2线程在T1之后执行,并且T3线程在T2之后执行?

在多线程中有多种⽅法让线程按特定顺序执⾏,你可以⽤线程类的join()⽅法在⼀个线程中启动另⼀个线程,另外⼀个线程完成该线程继续执⾏。为了确保三个线程的顺序你应该先启动最后⼀个(T3调⽤T2,T2调⽤T1),这样T1就会先完成⽽T3最后完成。

实际上先启动三个线程中哪⼀个都⾏,

因为在每个线程的run⽅法中⽤join⽅法限定了三个线程的执⾏顺序。

public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            public void run() {
                System.out.println("1");
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                System.out.println("2");
            }
        };
        Thread t3 = new Thread() {
            public void run() {
                System.out.println("3");
            }
        };
        t1.start();
        t1.join();//阻塞住直到t1完成
        t2.start();
        t2.join();
        t3.start();
        System.out.println("end");
    }
  1. volatile关键字

  • 保持内存可见性:所有线程都能看到共享内存的最新状态。

  • 防止指令重排

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值