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.Scanner;

public class ThreadTest06 {
    public static void main(String[] args) {
        TimeLock lock = new TimeLock();
        lock.start();
    }
}


class TimeLock extends Thread{
    private int time = 0;

    @Override
    public void run() {
        System.out.println("请设置倒计时器的时间 time (S/秒)");
        time = new Scanner(System.in).nextInt();
        int i=0,j=0;
        while (time>0){
            time--;
            i++;
            if (i==10){
                j+=i;
                System.out.println("时间已经过去了"+j+"秒,"+"还剩"+time+"秒");
                i=0;
            }
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(time);
        }
        System.out.println("计时结束!!!");
    }
}

运行示例:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风煞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值