JavaSE多线程练习题

简答题1

1.java中有几种方法可以实现一个线程(jdk5.0之前)?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

有两种实现方法,分别是继承Thread类与实现Runnable接口。
用synchronized关键字修饰同步方法,
反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。

2.sleep() 和 wait() 有什么区别?

解:相同点:一旦执行此方法,都可以使得当前线程进入阻塞状态
不同点:两个方法声明的位置不同:Thread 类中声明 sleep(),Object 类中声明 wait()
调用的要求不同: sleep() 可以在任何需要的场景下调用,wait() 必须使用在同步代码块或同步方法中
关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep() 不会释放锁,wait() 会释放锁

3.同步和异步有何异同,在什么情况下分别使用他们?举例说明

解:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率

4.启动一个线程是用run()还是start()?

解:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法就是正常的对象调用方法的执行,并不是使用分线程来执行的。

5.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

解:不能,一个对象的一个synchronized方法只能由一个线程访问。

6.请说出你所知道的线程同步的方法

解:wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

7.多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?

解:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
同步的实现方面有两种,分别是synchronized,wait与notify

自己的一些想法
多线程有四种实现方法:继承Thread类,实现Runnable接口,实现Callable接口,线程池
同步的实现方式:同步代码块,同步方法,Lock锁

8.线程的基本概念、线程的基本状态以及状态之间的关系

解:线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身

Java中的线程有四种状态分别是:创建、就绪、运行、阻塞、结束

在这里插入图片描述
9.简述synchronized和java.util.concurrent.locks.Lock的异同 ?

解:主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放

另一种的解法

Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

判断

1.C 和 Java 都是多线程语言 错误
解析:C 是单线程语言 有些疑问,不知道从那个角度说明的。

2.如果线程死亡,它便不能运行 正确
解析:线程死亡就意味着它不能运行

3.在 Java 中,高优先级的可运行线程会抢占低优先级线程 正确
解析:线程优先级的使用

4.程序开发者必须创建一个线程去管理内存的分配 错误
解析:Java 提供了一个系统线程来管理内存的分配

5.一个线程在调用它的 start 方法,之前,该线程将一直处于出生期 正确
解析:出生期的概念

6.当调用一个正在进行线程的 stop()方法时,该线程便会进入休眠状态 错误
解析:调用sleep()方法,使得该线程进入休眠状态

7.如果线程的 run 方法执行结束或抛出一个不能捕获的例外,线程便进入等待状态 错误
解析:如果线程的 run 方法执行结束或抛出一个不能捕获的例外,线程便进入死亡状态

8.一个线程可以调用 yield 方法使其他线程有机会运行 正确
解析:yield 方法总是让高优先级的就绪线程先运行

填空

1.Java 语言中提供了一个 (垃圾收集)线程,自动回收动态分配的内存

2.当(run)方法终止时,能使线程进入死亡状态

3.用(setPrority)方法可以改变线程的优先级

4.线程通过(yield)方法可以使具有相同优先级线程获得处理器

5.线程通过(sleep)方法可以休眠一段时间,然后恢复运行

6.(notify)方法使对象等待队列的第一个线程进入就绪状态

7.方法 resume( )负责重新开始(被 suspend( )方法停止)线程的执行

8.(sleep( ), wait( ), suspend())方法可以用来暂时停止当前线程的运行

简答题2

1.Java为什么要引入线程机制,线程、程序、进程之间的关系是怎样的。

解:线程可以彼此独立的执行,它是一种实现并发机制的有效手段,可以同时使用多个线程来完成不同的任务,并且一般用户在使用多线程时并不考虑底层处理的细节

程序是一段静态的代码,是软件执行的蓝本。进程是程序的一次动态执行过程,即是处于运行过程中的程序

线程是比进程更小的程序执行单位,一个进程可以启动多个线程同时运行,不同线程之间可以共享相同的内存区域和数据。多线程程序是运行时间后嗣可能出现在一个进程之内的、有一个以上线程同时运行的情况的程序

2.Runnable接口包括哪些抽象方法?Thread类有哪些主要域和方法?

解:Runnable接口中仅有run()抽象方法

Thread类主要域有:MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY

主要方法有start(),run(),sleep(),currentThread(),setPriority(),getPriority(),join()等

3.创建线程有哪两种方式(jdk5.0之前)?试写出每种的具体的流程。比较两种创建方式的不同,哪个更优

解:

继承Thread类
1.创建一个继承于Thread类的子类
2.重写Threa类中的run()  将此线程执行的操作声明在run()3.创建Thread类的子类的对象
4.通过此对象调用strat()启动当前线程;调用当前程序的run()

实现Runnable 接口
1.创建Runnable接口的实现类
2.实现类重写Runnable接口中的抽象方法:run()
3.创建此实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,并创建Thread类的对象
5.通过Thread类的对象调用start()

区别:
继承Thread: 线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。

实现 Runnable接口方法的好处
1)避免了单继承的局限性
2)多个线程可以共享同一个接口子类的对象,非常适合多个相同线程来处理同一份资源

编程

1.利用多线程设计一个程序,同时输出 50 以内的奇数和偶数,以及当前运行的线程名

继承Thread类的代码实现

package com.aiguigu.exer;

public class multithreading1 {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();

        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.start();
    }
}

class MyThread1 extends Thread{
    //输出50以内的偶数
    @Override
    public void run() {
        for(int i = 0; i < 50;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() +
                        ":" + i);
            }
        }
    }
}

class MyThread2 extends Thread{
    //输出50以内的奇数
    @Override
    public void run() {
        for(int i = 0; i < 50;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() +
                        ":" + i);
            }
        }
    }
}

实现Runnable接口的代码实现

package com.aiguigu.exer;

public class multithreading2 {
    public static void main(String[] args) {
        MyThread3 t3 = new MyThread3();
        MyThread4 t4 = new MyThread4();

        Thread w1 = new Thread(t3);
        Thread w2 = new Thread(t4);

        w1.start();
        w2.start();
    }
}

class MyThread3 implements Runnable{
    //实现50以内的偶数
    @Override
    public void run() {
        for(int i = 0; i < 50;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() +
                        ":" + i);
            }
        }
    }
}

class MyThread4 implements Runnable{
    //实现50以内的奇数
    @Override
    public void run() {
        for(int i = 0; i < 50;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() +
                        ":" + i);
            }
        }
    }
}

**2.定义两个线程(一个用继承Thread类,一个用实现Runnable接口),定义一个测试类包括一个主函数调用两个子线程(具体实现自定) **

具体功能:Thread类输出 张三 + 次数 ,实现Runnable接口输出李四 + 次数,输出当前线程名

package com.aiguigu.exer;

public class Exer2 {
    public static void main(String[] args) {
        ThreadExer2 te2 = new ThreadExer2();

        RunnableExer2 re2 = new RunnableExer2();
        Thread tre2 = new Thread(re2);

        te2.setName("Thread");
        tre2.setName("Runnable");

        te2.start();
        tre2.start();
    }
}

class ThreadExer2 extends Thread{
    @Override
    public void run() {
        for(int i = 1; i < 20;i++){
            System.out.println(Thread.currentThread().getName() +
                    ":张三" + i + "次");
        }
    }
}

class RunnableExer2 implements Runnable{
    @Override
    public void run() {
        for(int j = 1; j < 20;j++){
            System.out.println(Thread.currentThread().getName() +
                    ":李四" + j + "次");
        }
    }
}

3.在多线程中,为什么要引入同步机制?

多个线程执行的不确定性引起执行结果的不稳定,多个线程操作共享数据,会造成操作的不完整性,会破坏数据

4.wait()、notify()、notifyAll()的作用分别是什么?

wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器

notify():一旦执行此方法,就会唤醒被 wait 的一个线程。如果有多个线程被 wait,就会唤醒优先级高的线程

notifyAll():一旦执行此方法,就会唤醒所有被 wait 的线程

说明: wait(),notify(),notifyAll()

  • 三个方法必须使用在同步代码块或同步方法中
  • 三个方法的调用者必须是在同步代码块或同步方法中的同步监视器,否则,会出现异常
  • 三个方法是定义在java.long.Object类中

5.实现一个由A、B、C三个窗口同时销售100张票的系统,要求打印出每个窗口打印的售票情况,并且每个窗口不得连续售票

提示:

  • 明确哪些代码是多线程运行代码,须写入run()方法
  • 明确什么是共享数据
  • 明确多线程运行代码中哪些语句是操作共享数据的

使用同步代码块解决实现Runnable接口的线程安全问题,并添加 notify(),wait() 实现线程通信

package com.aiguigu.exer;

public class Exer3 {
    public static void main(String[] args) {
        Windows  w = new Windows();


        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("A");
        t2.setName("B");
        t3.setName("C");

        t1.start();
        t2.start();
        t3.start();
    }
}

class Windows implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while(true){

            synchronized(this){

                notify();

                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() +
                            ":卖票,票号为" + ticket);
                    ticket--;

                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    break;
                }

            }

        }
    }
}

使用同步方法解决实现Runnable接口的线程安全问题,并添加 notify(),wait() 实现线程通信

package com.aiguigu.exer;

public class Exer4 {
    public static void main(String[] args) {
        Windows4 w = new Windows4();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("A");
        t2.setName("B");
        t3.setName("C");

        t1.start();
        t2.start();
        t3.start();
    }
}

class Windows4 implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        boolean isFlag = true;
        while(isFlag){
            isFlag = show();
        }
    }

    private synchronized boolean show(){

        notify();
        if(ticket > 0){
            System.out.println(Thread.currentThread().getName() +
                    ":卖票,票号为" + ticket);
            ticket--;

            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return true;
        }else{
            return false;
        }
    }
}

使用Lock锁解决实现Runnable接口的线程安全问题,并添加 notify(),wait() 实现线程通信

没写出来 ,Lock锁中好像不能使用 notify(),wait() 方法

参考文献

链接: B站尚硅谷宋红康Java.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值