zoukankan      html  css  js  c++  java
  • Java 多线程详解

    一.重点

    1.创建和启动线程
    2.实现线程调度
    3.实现线程同步
    4.实现线程通信

    1.为什么要学习多线程?
    当多个人访问电脑上同一资源的时候,要用到多线程,让每个人感觉很多电脑同时为多个人服务。
    比如:
    1.1.排队叫号系统,多个人同一时间请电脑生成一张等待票据时,
    如果没有多线程的话,有可能会生成同一等待号的票据。
    1.2.两个乘客拿到同一张火车票或飞机票。

    2.什么是线程?


    先了解线程与进程的区别。


    1.一个进程包含多个线程
    2.cup执行的是线程,而不是进程
    3.线程是共享资源的,进程是独立占用资源的。
    4.线程虚拟瓜分计算机资源,而进程是真实的瓜分计算机资源。
    线程的定义:线程被认为带有自己的程序代码和数据的虚拟处理机的封装。


    结论:线程是强占式操作的。

    3.线程的创建与启动。


    定义线程有两种方式:
    3.1 继承java.lang.Thread类
    3.2 实现java.lang.Runnable接口 节约一个宝贵的继承机会,因为Java是单继承。
    使用线程的步骤:
    3.1 定义一个线程,同时指明这个线程所要执行的代码。
    3.2 创建线程对象
    3.3 启动线程
    线程的生命周期:有四个状态:
    3.1 新生状态、
    3.2 可运行状态、
    3.3 阻塞状态
    3.4 死亡状态

    解决线程的抢占执行有如下三种:
    4.线程调度
    线程调度的方法:
    4.1 join()方法
    4.2 sleep()方法
    4.3 yield()方法

    5.线程同步
    需要注意,同步不是同时,同步其实指的排队执行,异步反而指的时同时操作
    5.1 同步方法( synchronized )
    5.2 同步代码块( synchronized)

    public class TestAccount implements Runnable {
    // 所有用TestAccount对象创建的线程共享同一个帐户对象
    private Account acct = new Account();
    public void run() {
    for (int i = 0; i < 5; i++) {
    makeWithdrawal(100);// 取款
    if (acct.getBalance() < 0) {
    System.out.println("账户透支了!");
    }
    }
    }
    private void makeWithdrawal(int amt) {
    if (acct.getBalance() >= amt) {
    System.out.println(Thread.currentThread().getName() + " 准备取款");
    try {
    Thread.sleep(500);// 0.5秒后实现取款
    } catch (InterruptedException ex) {
    }
    // 如果余额足够,则取款
    acct.withdraw(amt);
    System.out.println(Thread.currentThread().getName() + " 完成取款");
    } else {
    // 余额不足给出提示
    System.out.println("余额不足以支付 " + Thread.currentThread().getName()
    + " 的取款,余额为 " + acct.getBalance());
    }
    }
    }

     

    6.线程通信
    线程通信的方法:
    wait();
    notify();
    notifyAll();


    生产者与消费者问题。



    二.线程定义

    1 何为线程
    在一个进程程序中运行的多条分支,这些分支会被cpu轮流切换执行,这些分支我们就叫线程

     


    2 进程vs线程
    进程是一个完整的程序应用,由操作系统去创建执行,进程之间是相互独立的,各自有自己的内存空间,互相之间不能访问各自的内存。例如我们打开记事本,就开启了记事本进程,我们打开酷狗,实际就运行了一个酷狗进程,记事本和酷狗进程是不能互相访问,CPU在调度切换执行不同进程,由于速度太快,感觉像是同时在进行
    线程是进程内部的多路分支,这些分支共享进程的整个内存,所以线程是可以互相访问,也经常容易造成数据冲突,跟进程一样,线程也是由CPU在高速的切换执行

    3 为什么需要线程
    1)多线程改善交互性
    2)提高性能
    cpu是非常强大,我们平常只是使用部分,为了更大程度利用cpu,我们可以开辟多个线程执行,cpu执行更多的任务

    三.定义线程的2种方式

    (1)继承Thread

    (2)实现Runnable

     

    为什么提供继承Thread的方式,又要提实现Runnable的方式?
    为了节约一个宝贵的继承机会


    Hero extends Spriter implements Runnable
    Tree extends Spriter implements Runnable


    四.线程运行的原理

     

     

    线程的三要素:
    1 执行代码
    2 CPU资源
    3 时间片

    start()启动线程,然后start()内部就会去调用线程run()

     

    五.线程的名字

    Thread(String name)
    setName(String name)
    getName()
    Thread.currentThread()

    六.线程的优先级

    1 多线程情况下,是平等的去抢夺cpu资源,换句话说,大家的优先级相同

    2 我们可以设置线程的优先级
    高 - 被cpu执行的概率要高(抢夺cpu的能力强)
    低 - 被cpu执行的概率要低(抢夺cpu的能力弱)

    3 Thread类提供三种优先级常量
    public static final int MAX_PRIORITY 10
    public static final int MIN_PRIORITY 1
    public static final int NORM_PRIORITY 5 (默认)

    4 API
    setPriority(int priority)
    getPriority()

    5 垃圾回收器
    后台的一个低优先级的线程,该线程会不定时扫描垃圾对象

     

     

    七.线程状态

      (1)线程状态总述

    对象
    创建--->消亡

    线程

     

    1 创建状态 (新生状态)
    刚刚new出来,在堆内存中分配内存空间
    2 就绪状态 (可运行状态)
    表示线程已经做好准备,可以被cpu执行,等待cpu分配时间片执行
    3 运行状态
    被cpu选中执行,是一种正在执行的状态,此时会执行run()方法内部的代码
    4 阻塞状态
    相当于一种休眠状态,被动的放弃cpu执行的机会,比如由IO流阻塞导致,或者由另一个线程合并导致
    5 等待池等待状态
    也是主动放弃cpu执行的机会,让给其它线程,需要等待其它的线程通知唤醒
    6 锁池等待
    多个线程同时执行同一份代码,如果想完整的执行完这段代码,然后才轮到另一个线程执行
    此时就可对这份代码上锁,先获得锁的线程先执行,其它线程就在锁池里等待,等待正在执行的线程释放锁
    7 结束(消亡)
    当线程的run()方法执行完,或者调用stop()停止时,线程就进入到消亡状态

    (2)就绪状态(可运行状态)start

    start()方法让一个线程做好准备,进入到就绪状态,还不会立刻执行,可运行状态

    (3)运行状态

    1.run

    当cpu调用到该线程时,那该线程就处于运行状态,运行状态中执行run()方法里面的代码

    2.yield

    如果此时yield的话则会进入就绪状态

    (4)阻塞

    1.sleep

    2.join

    等待调用该方法的线程结束后再继续执行本线程
    合并,如果A线程join到B线程,就会导致B线程进入阻塞状态

     

     

    3.io

    (5)等待

    1.线程同步的数据安全问题

    数据冲突发生的条件
    1 多线程
    2 多个线程访问操作同一个数据

     

     

    2.同步锁和锁池

    同步锁
    相对于异步来说
    需要注意,同步不是同时,同步其实指的排队执行,异步反而指的时同时操作

    锁池
    当多线程同时执行一段上了锁的代码时,获得锁的线程可以执行,其它线程就只能在锁池等待释放锁
    一旦线程释放锁,大家又回到同一起跑线,开始竞争锁

    3.Synchronized代码块

    1 如果一段代码会访问操作一个共同的数据,我们将这段代码用synchronized锁住,这段代码块就称为同步代码块
    synchronized (lock) {
    //代码
    }

    2 注意
    上的锁一定是同一把,如果不是同一把,多线程操作仍然可能会出问题。因为多把锁,每个线程都可能获得一把锁
    lock一定是同一个对象

    3 项目举例

    public Ticket takeTicket(String businessCode,String ip){
    synchronized (lock) {
    //1 按照业务类型查得该业务类型今天的最大号
    select max(no) from ticket where business_code=? and take_date between ? and ?;
    //2 判断max(no)有没有达到最大号
    if(max(no) <= business_limit){

    //3 生成号码对象
    Ticket t = new Ticket();
    t.setNo(max(no)+1);
    t.setBusinessCode(?);
    t.setTakeIp(ip);
    。。。。


    //4 将新生成的号插入数据库
    insert(t);
    //5 返回
    return t;
    }

    }

    4.Synchronized方法

    1 当我们锁住的代码外面没有更多的其它代码时,我们可以将synchronized放到放到方法的声明上
    public synchronized void fn(){
    }

    2 synchronized方法锁住锁是当前对象,就是this
    这里比较容易出问题,当创建多个对象时,就会有多个this,每个this都是不同的锁,达不到同步的效果了

    3 synchronized代码块比synchronized方法能够控制更细的粒度

    5.死锁

    发生在已经获得一把,又像获得另一锁,锁嵌套的问题
    synchronized(lock1){
    synchronized(lock2){

    }
    }

     

     

    死锁只能避免,不能解决

    6.synchronized的优缺点

    优点
    保证共享数据安全

    缺点
    消耗更多资源,效率低

    (6)消亡

    调用stop
    run()执行完成

    进入消亡的状态的线程不能再启动了,等待垃圾回收

     

     

     

    八.线程通信

    (1)wait

    对象监视器 - 实际上就是synchrozied锁住的锁对象
    只有在synchrozied锁住的锁对象上才可以调用wait方法,否则会报错
    调用wait()的线程会导致等待,此时会释放掉锁,让其它线程获得锁

    (2)notify

    (3)生产者&消费者

    当生产者生产的产品需要放置在一个空间,而这个空间是资源有限的时候,就需要等待(空间满时)
    当消费者消费产品时,如果没有产品可以消费,那么此时也需要等待

    九.同步集合

    1 同步集合也叫线程安全的集合,在多线程操作时不会发生数据冲突问题,以前我们学习的ArrayList这种集合是线程不安全的,在多线程情况下会发生问题

     

    //private static List list = new ArrayList();//线程不安全
    //private static List list = new Vector();//线程安全
    //建议使用下面的方式得到一个同步集合,在效率上要比Vector高一点
    private static List list = Collections.synchronizedList(new ArrayList());//线程安全
    2

  • 相关阅读:
    MFC Windows 程序设计>WinMain 简单Windows程序 命令行编译
    AT3949 [AGC022D] Shopping 题解
    CF643D Bearish Fanpages 题解
    CF643C Levels and Regions 题解
    CF241E Flights 题解
    CF671C Ultimate Weirdness of an Array 题解
    CF1592F Alice and Recoloring 题解
    GYM 102452E 题解
    CF494C Helping People 题解
    P5556 圣剑护符
  • 原文地址:https://www.cnblogs.com/Transkai/p/10487076.html
Copyright © 2011-2022 走看看