IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> javaEE组第九周考核任务 ==主要部分:多线程知识总结及相关练习== -> 正文阅读

[Java知识库]javaEE组第九周考核任务 ==主要部分:多线程知识总结及相关练习==

一、概念题

1. 简述程序、进程和线程之间的关系,什么是多线程程序?

程序、进程和线程之间的关系:

程序: 程序是一组有序指令的集合,并存放于某种介质中,是一个静态概念。

进程: 进程是为使程序能并发执行,且为了对并发执行的程序加以描述和控制而引入的概念。

在了解进程前,先说明下进程实体。进程实体是由程序段、相关的数据段和PCB三部分组成,是一个能独立运行、独立分配资源和独立接受调动的基本单位。

现在来说明进程的定义:进程是进程实体的一次执行过程,是系统进行资源分配和调度的一个独立单位。它是一个动态概念。

线程: 随着多处理机系统的发展,提高程序的并发执行程度的要求越来越高。为提高系统的并发执行度,进而引入了线程的概念。线程是比进程更小的能独立运行的基本单位(故又称“轻型进程”),更好的提高了程序的并发执行程度充分发挥了多处理机的优势。

多线程程序:

多线程:在同一个进程中同时运行的多个任务

举个例子,多线程下载软件,可以同时运行多个线程,但是通过程序运行的结果发现,每一次结果都不一致。 因为多线程存在一个特性:随机性。造成的原因:CPU在瞬间不断切换去处理各个线程而导致的,可以理解成多个线程在抢CPU资源。

2. 线程有哪五个基本状态?他们之间如何转化?简述线程的生命周期。

线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。

1.新建状态(New): 当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

2.就绪状态(Runnable)

一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。

3.运行状态(Running)

当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.

4.阻塞状态(Blocked)

线程运行过程中,可能由于各种原因进入阻塞状态: ? (1)线程通过调用sleep方法进入睡眠状态; ? (2)线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者; ? (3)线程试图得到一个锁,而该锁正被其他线程持有; ? (4)线程在等待某个触发条件;

所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

5.死亡状态(Dead)

有两个原因会导致线程死亡: ? (1) run方法正常退出而自然死亡, ? (2) 一个未捕获的异常终止了run方法而使线程猝死。 ? 为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

3. Runable接口中包括哪些抽象方法?Thread类中有哪写主要的成员变量和方法?

Runable接口中包括的抽象方法:

Runnable 接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为 run()

Thread类中主要的成员变量和方法

变量(属性):

private static native void registerNatives();
 ? ?static {
 ? ? ? ?registerNatives();
 ?  }
?
 ? ?private volatile String name;
 ? ?private int ? ? ? ? ? ?priority;
 ? ?private Thread ? ? ? ? threadQ;
 ? ?private long ? ? ? ? ? eetop;
?
 ? ?/* Whether or not to single_step this thread. */
 ? ?private boolean ? ? single_step;
?
 ? ?/* Whether or not the thread is a daemon thread. */
 ? ?private boolean ? ? daemon = false;
?
 ? ?/* JVM state */
 ? ?private boolean ? ? stillborn = false;
?
 ? ?/* What will be run. */
 ? ?private Runnable target;
?
 ? ?/* The group of this thread */
 ? ?private ThreadGroup group;
?
 ? ?/* The context ClassLoader for this thread */
 ? ?private ClassLoader contextClassLoader;
?
 ? ?/* The inherited AccessControlContext of this thread */
 ? ?private AccessControlContext inheritedAccessControlContext;
?
 ? ?/* For autonumbering anonymous threads. */
 ? ?private static int threadInitNumber;
 ? ?private static synchronized int nextThreadNum() {
 ? ? ? ?return threadInitNumber++;
 ?  }
?
 ? ?/* ThreadLocal values pertaining to this thread. This map is maintained
 ? ? * by the ThreadLocal class. */
 ? ?ThreadLocal.ThreadLocalMap threadLocals = null;
 ? ?/*
 ? ? * InheritableThreadLocal values pertaining to this thread. This map is
 ? ? * maintained by the InheritableThreadLocal class.
 ? ? */
?
 ? ?ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
?
 ? ?/*
 ? ? * The requested stack size for this thread, or 0 if the creator did
 ? ? * not specify a stack size.  It is up to the VM to do whatever it
 ? ? * likes with this number; some VMs will ignore it.
 ? ? */
 ? ?private long stackSize;
?
 ? ?/*
 ? ? * JVM-private state that persists after native thread termination.
 ? ? */
 ? ?private long nativeParkEventPointer;
?
 ? ?/*
 ? ? * Thread ID
 ? ? */
 ? ?private long tid;
?
 ? ?/* For generating thread ID */
 ? ?private static long threadSeqNumber;
?
 ? ?/* Java thread status for tools,
 ? ? * initialized to indicate thread 'not yet started'
 ? ? */
?
 ? ?private volatile int threadStatus = 0;
 ?volatile Object parkBlocker;
?
 ? 
 ? ?private volatile Interruptible blocker;
 ? ?private final Object blockerLock = new Object();
    //线程优先级
 ? ?public final static int MIN_PRIORITY = 1;
?
 ? ?public final static int NORM_PRIORITY = 5;
?
 ? ?public final static int MAX_PRIORITY = 10;
 ? ?public enum State {// 线程状态
 ? ? ? 
 ? ? ? ?NEW,
?
 ? ? ? ?RUNNABLE,
?
 ? ? ? ?BLOCKED,
?
 ? ? ? ?WAITING,
?
 ? ? ? ?TIMED_WAITING,
 ? ? ? ?
 ? ? ? ?TERMINATED;
 ?  }

方法:

(1)新建线程:

Thread t1 = new Thread();
t1.start();

新建线程,应该调用start()方法启动线程;如果直接调用run()方法,该方法也会执行,但会被当做一个普通的方法,在当前线程中顺序执行;而如果使用start()方法,则会创建一个新的线程执行run()方法。

(2)线程中断:

public void interrupt();
public boolean isInterrupted();
public static boolean interrupted();

三个方法很相似,线程中断只是通知目标线程有人希望你退出,而并不是使目标线程退出。 第一个方法是通知目标线程中断,即设置目标线称的中断标志位; 第二个方法判断当前线程是否被中断,如果被中断(即中断标志位被设置),则返回true,否则返回false; 第三个方法判断当前线程的中断状态,并清除该线程的中断标志位(也就意味着,如果连续调用两次该方法,并且中间没有再次设置中断标志位,第二次会返回false,因为中断标志位已经被清除)。

public static native void sleep(long millis) throws InterruptedException;

sleep()方法会将当前线程休眠若干ms,如果在休眠期间被调用interrupt()方法,则会抛出InterruptedException异常。如下:

public class TestThread implements Runnable{
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?while(true) {
 ? ? ? ? ? ?if(Thread.currentThread().isInterrupted()){ //如果当前线程已经被设置了中断标志位,则返回true
 ? ? ? ? ? ? ? ?System.out.println("Interrupted");
 ? ? ? ? ? ? ? ?break;
 ? ? ? ? ?  }
?
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?Thread.currentThread().sleep(1000);
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?System.out.println("Interruted when sleep!");
 ? ? ? ? ? ? ? ?Thread.currentThread().interrupt(); //Thread.sleep()方法由于中断而抛出异常,此时,它会清除中断标记;
 ? ? ? ? ?  }
?
 ? ? ? ? ? ?Thread.yield();
 ? ? ?  }
 ?  }
?
 ? ?public static void main(String[] args){
 ? ? ? ?Thread t1 = new Thread(new TestThread());
 ? ? ? ?t1.start();
?
 ? ? ? ?t1.interrupt(); //设置目标线程的中断标志位,中断标志位表示当前线程已经被中断了
 ?  }
}

(3)等待(wait)和通知(notify):

public final void wait() throws InterruptedException;
public final native void notify();
public final native void notifyAll();

obj.wait()是设置当前线程在该对象上等待,直到有线程调用obj.notify()方法(或notifyAll()方法)。当调用wait()方法后,该线程会进入一个等待队列,等待队列中可能有多个线程,notify()会随机唤醒其中一个线程,而notifyAll()会唤醒所有线程。 wait()和notify()方法必须在sychronized代码块中,调用这些方法时都需要先获得目标对象的一个监视器,然后调用这些方法时会释放监视器 与sleep不同的是,sleep()会一直占有所持有的锁,而wait()会释放锁。

(4)等待线程(join)和谦让(yield)

public final void join() throws InterruptedException;
public static native void yield();

如果一个线程的执行需要另一个线程的参与(比如当前线程执行需要另一个线程执行完毕才能继续执行),这时候可以调用join()方法。t1.join()方法表示等待线程t1执行完毕之后,当前线程再继续执行。当然也可以给join()设置时间参数。 注:join()的本质是让调用线程wait()在当前线程对象实例上,其部分源码如下:

while (isAlive()) {
 ? wait(0);
}

当线程执行完毕后,它会让被等待的线程在退出前调用notifyAll()通知所有等待的线程继续执行。因此不要在Thread对象实例上使用类似wait()或者notify()等方法。 yield()方法是使当前线程让出CPU,但该线程会再次抢夺CPU。

4. start()方法和run()方法的区别,sleep()和wait()的异同

start()方法和run()方法的区别:

/**
 * 那为什么不 cat.run(); 呢? 因为这样就只有main 线程了,因为cat.run();就只是一个简单的方法,并没有其他子线程
 * 源码:
 *1.      public synchronized void start() {
 *               start0();
 *          }
 *          // start0(); 是本地方法,是由 JVM 来调用的,底层为C/C++实现
 *          // 真正实现多线程的效果,是start0(),而不是 run
 * 2.      private native void start0();
 */

sleep()和wait()的异同:

首先sleep 是让线程休眠,到时间后会继续执行,wait 是等待,需要唤醒再继续执行,另外sleep 和wait 在多线程中还有许多不同:

使用方面: 从使用的角度来看sleep方法是Thread线程类的方法,而wait是Object顶级类的方法。 sleep可以在任何地方使用,而wait只能在同步方法和同步块中使用。 CPU及锁资源释放: sleep、wait调用后都会暂停当前线程并让出CPU的执行时间,但不同的是sleep不会释放当前持有对象的锁资源,到时间后会继续执行,而wait会释放所有的锁并需要notify/notifyAll后重新获取到对象资源后才能继续执行。 异常捕获方面: sleep需要捕获或者抛出异常,而wait/notify/notifyAll则不需要。

5. 如何在java程序中实现多线程?简述使用Thread字类和实现Runnable接口两种方法的异同。

一个类通过继承Thread类或者实现Runnable 接口就能够实现多线程

有经验的程序员都会选择实现Runnable接口 ,其主要原因有以下两点:

首先,java只能单继承,因此如果是采用继承Thread的方法,那么在以后进行代码重构的时候可能会遇到问题,因为你无法继承别的类了。

其次,如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

通过下面的实例可以清晰的看出两种方式的区别所在。

继承Thread 类:

package test;
 
public class main1 {
	public static void main(String[] args) {
		testThread mTh1=new testThread("A");
		testThread mTh2=new testThread("B");
		mTh1.start();
		mTh2.start();
	}
}

class testThread extends Thread{
	private int count=5;
	private String name;
    public testThread(String name) {
       this.name=name;
    }
	public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "运行  count= " + count--);
            try {
                sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
	}
}

实现Runnable 接口:

package test;
 
public class main2 {
	public static void main(String[] args) {
		testRunnable mTh = new testRunnable();
	        new Thread(mTh, "C").start();//同一个mTh,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常   
	        new Thread(mTh, "D").start();
	        new Thread(mTh, "E").start();
	}
}

class testRunnable implements Runnable{
    private int count=15;
	@Override
	public void run() {
		  for (int i = 0; i < 5; i++) {
			  System.out.println(Thread.currentThread().getName() + "运行  count= " + count--);
	            try {
	            	Thread.sleep((int) Math.random() * 10);
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	        }
	}
	
}

6. 了解lambada表达式(了解即可)

二、编程题

  1. 使用Thread.sleep()编写一个倒计时10秒的java程序,输出10,9,8....1;

    public class ThreadTest01 {
        public static void main(String[] args) {
            for (int i=10;i>=1;i--){
                System.out.print(i+"\t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    运行示例:

  2. 分别用Thread的方式和实现Runnable接口方法创建分线程,并遍历100以内的自然数

    继承Thread类:

public class ThreadTest02 {
    public static void main(String[] args) {
        Thread01 thread01 = new Thread01();
        thread01.start();
    }
}

class Thread01 extends Thread{

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            System.out.println(i);
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行示例:

实现Runnable接口:

public class ThreadTest02 {
    public static void main(String[] args) {
        Runnable01 runnable01 = new Runnable01();
        Thread thread = new Thread(runnable01);
        thread.start();
    }
}

class Runnable01 implements Runnable{

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            System.out.print(i+" ");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行示例:

3. 三个黄牛同时抢200张票,打印出哪个黄牛买了第几张票

package xiancheng.JavaEETest;

public class ThreadTest03 {
    public static void main(String[] args) {
        Ticket ticket01 = new Ticket();
        ticket01.setName("黄牛一");
        Ticket ticket02 = new Ticket();
        ticket02.setName("黄牛二");
        Ticket ticket03 = new Ticket();
        ticket03.setName("黄牛三");
        // 开抢
        ticket01.start();
        ticket02.start();
        ticket03.start();
    }
}

class Ticket extends Thread{
    private static int count=200;
    private static boolean loop=true;

    @Override
    public void run() {
        while (loop){
            seel();
        }
    }

    public synchronized static void seel(){
        if (count<=0){// 如果没票
            System.out.println("票已卖完!!!");
            loop=false;
        }
        else {// 如果还有票,暂停100毫秒
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到第"+count+"张票,"+"剩余"+(--count)+"张票");
        }
    }
}

运行示例:

4. 编写一个程序,启动三个线程,三个线程的ID分别是A,B,C;每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC…重复三遍

package xiancheng.JavaEETest;

public class ThreadTest04 {
    public static void main(String[] args) {
        for (int i=0;i<3;i++){
            TestID01 test1 = new TestID01();
            TestID02 test2 = new TestID02();
            TestID03 test3 = new TestID03();
            test1.setPriority(Thread.MAX_PRIORITY);
            test2.setPriority(Thread.NORM_PRIORITY);
            test3.setPriority(Thread.MIN_PRIORITY);
            test1.start();
            test2.start();
            test3.start();
            try {
                Thread.sleep(2500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("\n");
        }
    }
}

class TestID01 extends Thread{
    private int i=1;
    private char ID='A';
    @Override
    public void run() {
        while (i++<=5){
            System.out.print(ID);
            try {
                sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class TestID02 extends Thread{
    private int i=1;
    private char ID='B';
    @Override
    public void run() {
        while (i++<=5){
            System.out.print(ID);
            try {
                sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class TestID03 extends Thread{
    private int i=1;
    private char ID='C';
    @Override
    public void run() {
        while (i++<=5){
            System.out.print(ID);
            try {
                sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行示例:

三、挑战题

制作一个给定时间的倒计时器,在每个数字之间暂停1秒,每隔10个数字输出一个字符串

package xiancheng.JavaEETest;

import java.util.ArrayList;

public class ThreadTest05 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("javaEE");
        list.add("jact");
        list.add("Php");
        list.add("tom.class");
        list.add("冬奥会");
        Time time = new Time(list);
        time.start();
    }
}

@SuppressWarnings({"all"})
class Time extends Thread{
    private int time =50;
    private ArrayList<String> list =null;

    public Time(ArrayList<String> list ){
        this.list=list;
    }
    @Override
    public void run() {
        int i=0;
        while (time>=0){
            time--;
            i++;
            if (i==10){
                System.out.println(list.get(time/10));
                i=0;
            }
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行示例:

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-07 13:35:18  更:2022-02-07 13:35:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:34:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码