• 线程上


    第十章 线程

    10.1线程的概念:

    事实上,在单个程序内部是可以在同一时刻进行多种运算的,这就是所谓的多进程(这与多任务的概念有相似之处)。、

    一个单独的进程与顺序程序相似,也有一个入口,一个出口,以及一个顺序执行的序列。从概念上说,一个线程是一个程序内部的一个顺序控制流。线程并不是程序,它自己本身并不能运行,而必须在程序中运行。在一个程序中可以实现多个进程,这些进程同时运行,但是多线程并不等于多次启动一个程序,操作系统也不会把每个线程当做进程来对待。

    线程如进程的区别:

    1:两者的颗粒度不同,是两个不同层次上的概念。进程是由操作系统来管理的,而线程则是在一个程序(进程)内。

    2:不同的的代码,内部数据和状态都是完全独立的,而一个程序内的多线程共享同一块内存空间和同一组系统资源,有可能互相影响。

    3:线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换负担比进程切换有的要小。

    使用多线程具有的如下的优点:

    1:多线程编程简单,效率高(能直接共享数据和资源,而多进程不能)

    2:适合于开发服务程序(如web服务,聊天服务)

    3:适合于开发有多种交互接口的程序(如聊天程序的客户端,网路下载工具)

    4:适合于有人机交互又有计算量的程序

    5:减轻编写交互频繁,涉及面多的程序的困难(如监听网络接口)

    6:程序的吞吐量会得到改善

    7:有多个处理器的系统,可以并发运行不同的线程

    10.2线程的建立

    Java提供了类Java.lang.Thread来支持多线程编程,Thread类有以下的构造方法

    Thread()

    Thread(Runable target)

    Thread(Runable target,String name)

    Thread(String name)

    Thread(ThreadGroup group,Rnuable target)

    Thread(ThreadGroup group,Rnuable target,String name)

    Thread(ThreadGroup group,String name)

    参数target是线程执行的目标对象,即线程执行的代码;group是线程所在的组;name是线程的名字

    10.2.1采用继承法创建线程

    该方法比较简单,主要是通过继承java.lang.Thread,并覆盖Thread类的run()方法来完成线程的创建.Thread类是一个具体的类不是抽象类,该类封装了线程的行为.要创建一个线程,程序员必须创建一个Thread的子类.Thread类中最重要的方法时run()start().

    Run()方法必须进行重写,把线程所要执行的代码加入到这个方法中,也就是线程体.但是它必须经过start()方法来启动线程.

    public class MyThread extends Thread

    {

    // count变量用于统计打印的次数并共享变量

    private static int count = 0;

    public static void main(String[] args)

    {

    MyThread p = new MyThread("t1");

    // 线程执行

    p.start();

    // 主线程main方法执行一个循环

    for (int i = 0; i < 5; i++)

    {

    count++;

    System.out.println(count + " :main");

    }

    }

    // 构造方法

    public MyThread(String name)

    {

    // 调用父类的构造方法

    super(name);

    }

    @Override

    public void run()

    {

    // 线程中必须有的run方法

    for (int i = 0; i < 5; i++)

    {

    count++;

    System.out.println(count + ":" + this.getName());

    }

    }

    }

    10.2.2 通过实现接口创建线程

    该方法通过实现java.lang.Runnable接口的类来创建多线程.该接口定义了一个方法run(),所以必须的新类中实现它,但是Runnable接口中没有任何对线程的支持,还必须要创建Thread类的实例

    public class MyThread2 implements Runnable

    {

    public static void main(String[] args)

    {

    for (int i = 0; i < 5; i++)

    {

    new Thread(new MyThread2(i + 1)).start();

    }

    }

    int count, number;

    public MyThread2(int i)

    {

    this.number = i;

    System.out.println("创建线程 " + this.number);

    }

    public void run()

    {

    while (true)

    {

    System.out.println("线程 " + this.number + ":计数 " + this.count);

    if (++this.count == 6)

    {

    return;

    }

    }

    }

    }

    10.3线程的生命周期及调度

    10.3.1 线程的生命周期

    线程是动态的,具有一定的生命周期,分别从创建,执行,堵塞直到死亡.在每一个线程类中都定义了用于完成功能的run方法,这个方法称为线程体

    1:线程的四个状态

    1.1:创建状态:

    当利用new关键字创建线程对象实例后,它仅仅作为一个对象实例存在,JVM没有为其分配CPU时间片等线程资源

    1.2:就绪状态:

    当处于创建状态的线程中调用start方法将线程的状态转换为就绪状态.

    1.3:堵塞状态

    堵塞指的是暂停一个线程的执行以等待某个条件发生(如某资源准备就绪),若线程处于堵塞状态,调度机制不给它分配任何CPU时间,直接跳过它.

    1.4:死亡状态

    当线程运行结束或者在调用线程对象的stop方法后线程将终止运行,JVM收回线程占用的资源

    10.3.2:线程调度和优先级

    Java采用的是一种简单,固定的调度法,即固定优先级调度(抢先式调度),Java将线程的优先级分为10个等级,分别用110的数字表示.数字越大表明线程的级别越高.相应的,Thread类中定义了表示线程最高,最低,和普通优先级的常量MIN_PRIORITY,MAX_PRIORITY,NORMAL_PRIORITY,代表的优先级分别是1,10,5,当一个线程对象被创建时,其默认的优先级是5.

    在应用程序中设置线程优先级的方法比较简单,在创建线程对象后可以调用线程对象的setPriority()方法该表线程的优先级,同样可以调用getPriority()来获取当前线程的优先级.

    public class TestThreadPriority extends Thread

    {

    public static void main(String[] args)

    {

    TestThreadPriority t1 = new TestThreadPriority("Thread1");

    t1.setPriority(MIN_PRIORITY);

    t1.start();

    TestThreadPriority t2 = new TestThreadPriority("Thread2");

    t2.setPriority(NORM_PRIORITY);

    t2.start();

    TestThreadPriority t3 = new TestThreadPriority("Thread3");

    t3.setPriority(MAX_PRIORITY);

    t3.start();

    }

    public TestThreadPriority(String name)

    {

    super(name);

    }

    @Override

    public void run()

    {

    for (int i = 0; i < 3; i++)

    {

    System.out.println(this.getName() + " is running");

    }

    super.run();

    }

    }

    10.4:线程互斥

    在并发程序设计中已经被研究并得到解决.对多线程共享的资源或数据称为临界资源,而把每一个线程中访问临界资源的那一段代码称为临界代码.通过为临界带买段设置信号灯,就可以保证资源的完整性,从而安全地访问共享资源.

    为了实现这种机制,Java语言提供了以下两方面的支持

    为每个对象设置了一个互斥锁标记.该标记保证在任何时候,只能有一个线程有该互斥锁,其他线程如果需要获得互斥锁,必须等待当前拥有该锁的线程将其释放.该对象称为互斥对象.

    为了配合使用对象的互斥锁,Java语言提供了保留字synchronized.其基本用法如下:

    Synchronized(互斥对象){

    临界代码

    }

    当一个线程执行到该行代码时,首先检测该互斥对象的互斥锁.如果该互斥锁没有被占用,则该线程将获得该互斥锁,并执行临界代码,直到执行完毕并释放互斥锁;

    可以看出,任意一个对象都可以作为信息灯,从而解决上面的问题,首先定义一个互斥对象类,作为信号灯.由于该对象只作为信号量使用,所以并不需要为它定义其他方法.

    public class AccountThread extends Thread

    {

    public static void main(String[] args)

    {

    Account account = new Account(100);

    Semaphore semaphore = new Semaphore();

    AccountThread at1 = new AccountThread(account, 1000, semaphore);

    AccountThread at2 = new AccountThread(account, 0, semaphore);

    at1.start();

    at2.start();

    }

    Account account;

    int delay;

    Semaphore semaphore;

    // 构造方法

    public AccountThread(Account account, int delay, Semaphore semaphore)

    {

    this.account = account;

    this.delay = delay;

    this.semaphore = semaphore;

    }

    @Override

    public void run()

    {

    synchronized (this.semaphore)

    {

    if (this.account.balance >= 100)

    {

    try

    {

    // 延迟

    sleep(this.delay);

    // 模拟取钱100

    this.account.balance = this.account.balance - 100;

    System.out.println("withdraw 100 successful!!!");

    }

    catch (InterruptedException e)

    {

    e.printStackTrace();

    }

    }

    else

    {

    System.out.println("withdraw failed!!!");

    }}}}

    // 定义一个类,利用其对象作为互斥信号灯

    class Semaphore{}

  • 相关阅读:
    jQuerychicun
    css3动画
    app开发,H5+CSS3页面布局小tips
    函数基础
    函数
    冒泡排序
    关于Vue+iview的前端简单的导入数据(excel)
    关于Vue+iview的简单下拉框滚动加载
    ES6中set的用法回顾
    百度地图api设置点的自定义图标不显示
  • 原文地址:https://www.cnblogs.com/qingtianBKY/p/6020692.html
Copyright © 2020-2023  润新知