zoukankan      html  css  js  c++  java
  • 第十章 多线程

    1.程序、进程、线程的区别是什么? 举个现实的例子说明。(网上查资料,跟老师的
    不一样)


    程序(Program):是一个指令的集合。程序不能独立执行,只有被加载到内存中,系统为它分配资源后才能执行。
    进程(Process):如上所述,一个执行中的程序称为进程。
    进程是系统分配资源的独立单位,每个进程占有特定的地址空间。
    程序是进程的静态文本描述,进程是程序在系统内顺序执行的动态活动。
    线程(Thread):是进程的“单一的连续控制流程“。
    线程是CPU调度和分配的基本单位,是比进程更小的能独立运行的基本单位,也被称为轻量级的进程。
    线程不能独立存在,必须依附于某个进程。一个进程可以包括多个并行的线程,一个线程肯定属于一个进程。Java虚拟机允许应用程序并发地执行多个线程。

    举例:如一个车间是一个程序,一个正在进行生产任务的车间是一个进程,车间内每个从事不同工作的工人是一个线程。


    2.【上机】Java中通过哪些方式创建多线程类? 分别使用代码说明。并调用之。


    (1)自定义线程类继承Thread:
    public class MyDefinedThread extends Thread{
    //重写run()方法;
    public void run(){
    //把线程需要执行的任务写在run()方法里;
    }

    public static void main(String[] args){
    MyDefinedThread mdt=new MyDefinedThread();
    mdt.start(); //启动线程。
    }
    }
    (2) 自定义类实现Runnable接口;
    public class MyRunnable implements Runnable{
    //实现run()方法;
    public void run(){
    //把线程需要执行的任务写在run()方法里;
    }

    public static void main(String[] args){
    MyRunnable mr=new MyRunnable();
    Thread th=new Thread(mr);//”mr”并不是一个线程对象,而是要作为参数传递到Thread的构造方法中;“th“才是一个线程对象。
    th.start;//启动线程。
    }
    }


    3.Thread类有没有实现Runnable接口?


    有实现。


    4.当调用一个线程对象的start方法后,线程马上进入运行状态吗?


    不是,只是进入就绪(可运行)状态,等待分配CPU时间片。一旦得到CPU时间片,即进入运行状态。

    5.下面的代码,实际上有几个线程在运行:


    两个:线程t和main()方法(主线程)。
    public static void main(String[] argc) throws Exception {
    Runnable r = new Thread6();
    Thread t = new Thread(r, "Name test");
    t.start();
    }

    6.说说:sleep、yield、join方法的区别。


    sleep():在指定时间内让线程暂停执行,进入阻塞状态。
    在指定时间到达后进入就绪状态。线程调用sleep()方法时,释放CPU当不释放对象锁(如果持有某个对象的锁的话)。
    join(): 当前线程等待调用此方法的线程执行结束再继续执行。如:在main方法中调用t.join(),那main方法在此时进入阻塞状态,一直等t线程执行完,main方法再恢复到就绪状态,准备继续执行。
    yield(): 调用该方法的线程暂停一下,回到就绪状态。所以调用该方法的线程很可能进入就绪状态后马上又被执行。


    7.为什么不推荐使用stop和destroy方法来结束线程的运行?


    stop():此方法可以强行中止一个正在运行或挂起的线程。但stop方法不安全,就像强行切断计算机电源,而不是按正常程序关机。可能会产生不可预料的结果。举例来说:
    当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,
    假如一个线程正在执行:
    synchronized void {
    x = 3;
    y = 4;
    }
    由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记 线程的stop方法,以后我们再也不要说“停止线程”了。

    destroy():该方法最初用于破坏该线程,但不作任何资源释放。它所保持的任何监视器都会保持锁定状态。不过,该方法决不会被实现。即使要实现,它也极有可能以 suspend() 方式被死锁。如果目标线程被破坏时保持一个保护关键系统资源的锁,则任何线程在任何时候都无法再次访问该资源。如果另一个线程曾试图锁定该资源,则会出现死锁。


    8.【上机】写个代码说明,终止线程的典型方式。


    (1)当run()方法执行完后,线程就自动终止了。
    (2)但有些时候run()方法不会结束(如服务器端监听程序),或者其它需要用循环来处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){……}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。下面通过例子来说明:

    public class ThreadFlag extends Thread 

        public volatile boolean exit = false; 

        public void run() 
        { 
            while (!exit); 
        } 
        public static void main(String[] args) throws Exception 
        { 
            ThreadFlag thread = new ThreadFlag(); 
            thread.start(); 
            sleep(5000); // 主线程延迟5秒 
            thread.exit = true;  // 终止线程thread 
            thread.join(); 
            System.out.println("线程退出!"); 
        } 

    在上面代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值。


    9.A线程的优先级是10,B线程的优先级是1,那么当进行调度时一定会调用A吗?


    不一定。线程优先级对于不同的线程调度器可能有不同的含义,可能并不是用户直观的推测。


    10.【上机】模仿老师课堂例子,完成账户取钱的模拟操作代码。


    11.synchronize修饰在方法前是什么意思?


    一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.


    12.synchronize修饰的语句块,如下面的代码。是表示该代码块运行时必须获得
    account对象的锁。如果没有获得,会有什么情况发生?


    如果没有获得account对象的锁,就不能执行synchronize修饰的语句块,包括此语句块,还有其他需要获得此account对象锁才能执行的同步代码块和同步方法。
    synchronized (account) {
    if(account.money-drawingNum<0){
    return;
    }
    }

    13.【上机】死锁是怎么造成的?用文字表达。再写一个代码示例。


    过多的线程同步会引起死锁。如两个线程都在等待对方释放锁才能继续执行,有时会出现僵持局面:两个线程都持有对方需要的锁,而两个线程都需要对方释放锁了才能继续运行。举例来说:

    我们先看看这样一个生活中的例子:在一条河上有一座桥,桥面较窄,只能容纳一辆汽车通过,无法让两辆汽车并行。如果有两辆汽车A和B分别由桥的两端驶上该桥,则对于A车来说,它走过桥面左面的一段路(即占有了桥的一部分资源),要想过桥还须等待B车让出右边的桥面,此时A车不能前进;对于B车来说,它走过桥面右边的一段路(即占有了桥的一部分资源),要想过桥还须等待A车让出左边的桥面,此时B车也不能前进。两边的车都不倒车,结果造成互相等待对方让出桥面,但是谁也不让路,就会无休止地等下去。这种现象就是死锁。死锁是程序运行时出现的一种问题,是需要避免的。

    代码示例:
    publicclass DeadLock{
    Object A=new Object();
    Object B=new Object();

    publicvoid toEast(){
    synchronized(A){
    synchronized(B){
    System.out.println("向东行驶。");
    }
    }
    }
    publicvoid toWest(){
    synchronized(B){
    synchronized(A){
    System.out.println("向西行驶。");
    }
    }
    }
    }
    publicclass ToEastThread extends Thread {
    DeadLock d=new DeadLock();
    public ToEastThread(DeadLock d) {
    super();
    this.d = d;
    }
    publicvoid run(){
    d.toEast();
    }
    }
    publicclass ToWestThread extends Thread {
    DeadLock d=new DeadLock();
    public ToWestThread(DeadLock d) {
    super();
    this.d = d;
    }
    publicvoid run(){
    d.toWest();
    }
    }
    publicclass Test1 {
    publicstaticvoid main(String[] args) {
    DeadLock d=new DeadLock();
    ToEastThread et=new ToEastThread(d);
    ToWestThread wt=new ToWestThread(d);
    et.start();
    wt.start();
    }
    }


    14.使用Timer和TimerTask实现定时执行,定时在每天下午17:00执行。

    知识点简介:
    (1)Timer:定时器,实际上是个线程,定时调度所拥有的TimerTasks。
    (2)TimerTask:一个拥有run方法的类,需要定时执行的代码放到run方法体内。 TimerTask一般是以匿名类的方式创建。
    语法简介:
    java.util.Timer timer = new java.util.Timer(true);   
    // true 说明这个timer以daemon方式运行(优先级低,   
    // 程序结束timer也自动结束),注意,javax.swing   
    // 包中也有一个Timer类,如果import中用到swing包,   
    // 要注意名字的冲突。   
    TimerTask task = new TimerTask() {   
    public void run() {   
    ... //每次需要执行的代码放到这里面。   
    }   
    };  
    用法简介:
    //以下是几种调度task的方法:   
     timer.schedule(task, time);   
    // time为Date类型:在指定时间执行一次。   
     timer.schedule(task, firstTime, period);   
    // firstTime为Date类型,period为long   
    // 从firstTime时刻开始,每隔period毫秒执行一次。   
    timer.schedule(task, delay)   
    // delay 为long类型:从现在起过delay毫秒执行一次   
    timer.schedule(task, delay, period)   
    // delay为long,period为long:从现在起过delay毫秒以后,每隔period   
    // 毫秒执行一次。  
    举例:
    import java.util.TimerTask;
    publicclass TimePrintTask extends TimerTask {
    int i=1;
    publicvoid run(){
    System.out.println(i);
    i++;
    }
    }
    publicclass Test {
    publicstaticvoid main(String[] args) {
    Timer timer=new Timer();
    //timer.schedule(new TimePrintTask(), 1000, 500);
    DateFormat df=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SS");
    Date firstDate=null;
    try {
    firstDate=df.parse("2016/12/09 17:00:00 00");
    } catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    timer.schedule(new TimePrintTask(),firstDate , 1000);
    }
    }

    15.wait方法被调用时,所在线程是否会释放所持有的锁资源? sleep方法呢?


    wait:释放CPU,释放锁;
    sleep:释放CPU,不释放锁。


    16.wait、notify、notifyAll是在Object类中定义的方法吗?作用分别是什么?wait(),notify(),notifyAll()不属于Thread类,而是属于Object类,也就是说每个对象都有wait(),notify(),notifyAll()的功能。因为每个对像都有锁,锁是每个对像的基础,而wait(),notify(),notifyAll()都是跟锁有关的方法。

    三个方法的作用分别是:
    wait:导致当前线程等待,进入阻塞状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。当前线程必须拥有此对象监视器(对象锁)。该线程释放对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行.
    notify:唤醒在此对象监视器(对象锁)上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。此方法只应由作为此对象监视器的所有者的线程来调用.
    "当前线程必须拥有此对象监视器"与"此方法只应由作为此对象监视器的所有者的线程来调用"说明wait方法与notify方法必须在同步块内执行,即synchronized(obj之内).
    notifyAll: 唤醒在此对象监视器(对象锁)上等待的所有线程。


    17.notify是唤醒所在对象wait pool中的第一个线程吗?


    不是。调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择。

    18.【上机】使用线程通信实现生产者-消费者问题。

    详见课堂实例。

  • 相关阅读:
    数据库中 dbo是什么意思
    常用的设计模式 介绍
    ReferenceEquals和 == 和equals()的比较
    IOC 控制反转模式
    集群和负载均衡
    数据库 读写分离
    C# 中using的几个用途
    WCF ABC
    SQL Server数据库性能优化(三)之 硬件瓶颈分析
    SQL Server数据库性能优化(二)之 索引优化
  • 原文地址:https://www.cnblogs.com/ren549047861/p/11294158.html
Copyright © 2011-2022 走看看