线程

相当于轻量级进程,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态。

构建一个线程

    //创建一个新线程 参数为实现了 Runnable 接口的类实例
    Thread t = new Thread(run);
    //启动线程
    t.start();

线程终止

  • run方法完成自动退出
  • 一个没有捕获的异常终止了run方法而意外死亡
  • stop方法 已弃用
  • interrupt方法,重设中断状态,发出中断请求,如果该线程被sleep调用阻塞会抛出异常InterruptedException

线程状态

  • New 创建
  • Runnable 可运行
  • Blocked 被阻塞
  • Waiting 等待
  • Timed waiting 计时等待
  • Terminated 被终止

抢占式调度

一个进程处于Runnable状态,也可能没有运行,取决于操作系统给时间提供的运行时间

线程让步

暂停当前正在执行的线程对象,让其他线程先执行,
实际中无法保证yield()达到让步目的,
因为让步的线程还有可能被线程调度程序再次选中。

守护线程

    t.setDaemon(true);

注意:

不要直接调用run方法,这样不能启动新线程,当设计多线程应用程序的时候,一定不要依赖于线程的优先级,因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。

demo

    public class MyRunnable implements Runnable {
        public String name = null;

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

        public static void main(String[] args) {
            MyRunnable run = new MyRunnable();
            run.name = "aaaa";
            MyRunnable run2 = new MyRunnable();
            run2.name = "bbbbb";
            Thread t = new Thread(run);
            Thread t2 = new Thread(run2);
            /**
             * 设置优先级 1~10
             * Thread.MAX_PRIORITY
             * Thread.NORM_PRIORIT
             * Thread.MIN_PRIORITY
             */
            t.setPriority(8);
            /**
             * 设置一个线程异常处理器
             */
            t.setUncaughtExceptionHandler(new MyErrorHandler());
            /**
             * 启动线程
             */
            t.start();

            /**
             * 等调用join的线程执行完成再执行下面接着的线程
             */
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            /**
             * 设为守护线程 要在线程启动前调用
             */
            t2.setDaemon(true);
            t2.start();

            //用以判断当前线程是否为中断状态
            Thread.currentThread().isInterrupted();

            /**
             * 线程让步
             *      暂停当前正在执行的线程对象,让其他线程先执行,
             *      实际中无法保证yield()达到让步目的,
             *      因为让步的线程还有可能被线程调度程序再次选中。
             */
            Thread.yield();
        }
    }

    //异常处理器
    public class MyErrorHandler implements UncaughtExceptionHandler {
          /**
           * 这里可以做任何针对异常的处理,比如记录日志等等
           */
          public void uncaughtException(Thread a, Throwable e) {
            System.out.println("This is:" + a.getName() + ",Message:"
                + e.getMessage());
            e.printStackTrace();
          }
    }

同步

    /**
        当多个线程操作同一个对象时,容易出现数据异常状态,
        具体原因是当一个线程获取了所需要的参数,准备赋值时等待,
        另外一个线程也获取了所需要的参数进行了赋值
        这时第一个赋值被唤醒,其值会覆盖第二个线程的值
    */
    public class HeroProperties {
        public int mana = 1000;
        public void useSkill(){
            mana -= 10;
            System.out.println("剩余蓝:" + mana);
        }
    }

    public class SyncRunnable implements Runnable{
        public SyncRunnable(HeroProperties hero){
            this.hero = hero;
        }
        public HeroProperties hero;

        @Override
        public void run() {
            hero.useSkill();
        }
        public static void main(String[] args) {
            HeroProperties hero = new HeroProperties();
            for(int i=0;i<1000;i++){
                SyncRunnable r  = new SyncRunnable(hero);
                Thread t = new Thread(r);
                t.start();
            }
        }
    }
    /*
        打印的结果:出现重复数据
        剩余蓝:980
        剩余蓝:960
        剩余蓝:970
        剩余蓝:980
        剩余蓝:940
        剩余蓝:950
        剩余蓝:930
        剩余蓝:920
        ...
        剩余蓝:-100
        剩余蓝:-110
    */

锁对象-ReentrantLock

使用锁对象 使赋值的代码块变为同步

    /**
        创建锁
        Lock lock = new ReentrantLock();
        锁
        lock.lock();
        解锁
        lock.unLock();
    */

    public class HeroProperties {
        public int mana = 1000;
        public Lock lock = new ReentrantLock();
        public void useSkill(){
            //锁
            lock.lock();
            mana -= 10;
            System.out.println("剩余蓝:" + mana);
            //解锁
            lock.unlock();
        }
    }
    /*
        打印结果没有出现重复值
        剩余蓝:990
        剩余蓝:980
        剩余蓝:970
        剩余蓝:960
        剩余蓝:950
        剩余蓝:940
        剩余蓝:930
        剩余蓝:920
        ...
        剩余蓝:-100
        剩余蓝:-110

    */

条件锁-Condition

此时值出现负数是异常的,mana要大于0才能使用skill,要添加一个条件使mana小于0时进行等待mana的恢复,恢复之后再继续使用skill,此时就要使用条件锁,不符合条件是线程进入等待,又不会造成死锁。

    /**
        Lock lock = new ReentrantLock();
        Condition cd = lock.newCondition();
        //该条件不满足时调用await,线程进入等待
        cd.await();

        //其他线程执行完之后 激活所有等待的线程
        cd.signalAll();
        //激活随机一条线程
        cd.signal();
    */

    /**
        Lock lock = new ReentrantLock();
        Condition cd = lock.newCondition();
        //该条件不满足时调用await,线程进入等待
        cd.await();

        //其他线程执行完之后 激活所有等待的线程
        cd.signalAll();
        //激活随机一条线程
        cd.signal();
    */

    public class HeroProperties {
        public int mana = 100;
        public Lock lock = new ReentrantLock();
        public Condition cd = lock.newCondition();
        public void useSkill() throws InterruptedException{
            lock.lock();
            //自然恢复的蓝量
            mana+=1;
            System.out.println(Thread.currentThread() +"尝试使用技能---------"+ "蓝量:"+mana);
            if(mana<10) {
                System.out.println(Thread.currentThread() +"ZZZZZZZZZZZZ"+"线程睡眠");
                cd.await();
            }
            //有可能是被唤醒的线程
            if(mana<10) {
                //尝试唤醒的时候不一定能直接运行,也有可能被其他线程执行了操作导致该线程操作失败
                System.out.println(Thread.currentThread() +"随机唤醒的线程不符合条件");
            }else{
                //蓝条刚好只剩下一次的时候吃药
                boolean isUseItem = (mana==10);

                System.out.println(Thread.currentThread() + "蓝量:" + mana);
                mana -= 10;
                System.out.println(Thread.currentThread() + "使用技能后剩余蓝:" + mana);

                if(isUseItem) {
                    mana += 30;
                    System.out.println("吃药恢复30");
                }
                if(mana>=10) {
                    //唤醒所有线程
                    //cd.signalAll();
                    //随机唤醒一个线程
                    cd.signal();
                }
            }
            lock.unlock();
        }
    }

synchronized

可以在方法名添加 synchronized 关键字实现同步

    /**
     * wait()       线程进入等待
     * notify()     随机唤醒一个线程
     * notifyAll()  唤醒所有线程
     */
    public synchronized void useSkill(){
        //do something
        //wait();
    }

同步阻塞,synchronized一个对象组成一个代码块,该代码块就能实现同步

    Object lock = new Object()
    public void useSkill(){
        synchronized(obj){
            //do something
        }
    }

java基础(10) -线程的更多相关文章

  1. 【Java基础】线程和并发机制

    前言 在Java中,线程是一个很关键的名词,也是很高频使用的一种资源.那么它的概念是什么呢,是如何定义的,用法又有哪些呢?为何说Android里只有一个主线程呢,什么是工作线程呢.线程又存在并发,并发 ...

  2. Java基础-多线程-③线程同步之synchronized

    使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...

  3. Java基础篇——线程、并发编程知识点全面介绍(面试、学习的必备索引)

    原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10739579.html,希望大家多多支持!!! 一.线程基础 1.线程与进程 线程是指进程 ...

  4. Java基础_线程的使用及创建线程的三种方法

    线程:线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. 进程:进 ...

  5. Java基础之线程——使用执行器(UsingExecutors)

    控制台程序. 在这个版本的银行示例中,把借款和贷款事务创建为在不同线程中执行的任务,它们把事务提交给职员.创建事务的任务是Callable<>任务,因为它们需要返回已为每个账户创建的借款或 ...

  6. Java基础10:全面解读Java异常

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  7. Java基础8-多线程;同步代码块

    作业解析 利用白富美接口案例,土豪征婚使用匿名内部类对象实现. interface White{ public void white(); } interface Rich{ public void ...

  8. Java基础-多线程-①线程的创建和启动

    简单阐释进程和线程 对于进程最直观的感受应该就是“windows任务管理器”中的进程管理: (计算机原理课上的记忆已经快要模糊了,简单理解一下):一个进程就是一个“执行中的程序”,是程序在计算机上的一 ...

  9. java基础25 线程的常用方法、线程安全问题、死锁现象

    一.线程的常用方法 1.Thread(String name):初始化线程的名字2. setName(String name):设置线程的名字3. getName():返回线程的名字4. sleep( ...

随机推荐

  1. Window7 下 WNMP 环境搭建

    WNMP 指"Windows 下的 Nginx.MariaDB/MySQL 和 PHP 环境". 下载 Nginx:http://nginx.org/en/download.htm ...

  2. 【.net 深呼吸】通过标准输入/输出流来完成进程间通信

    实现进程之间煲电话粥的方式,有好几种,比如,你可以用这些方案: 1.使用socket来传递.这个好像很无聊,本地进程之间也用socket?不过,通过本机回环网络确实可以进程之间通信. 2.WCF,与上 ...

  3. Microsoft Azure IoTHub Serials 1 - 使用Android设备与Azure IoTHub进行交互

    Azure IoTHub的目标是为物联网的应用场景提供方便的设备接入,完成消息的发送和接收(C2D和D2C).经过持续不断的努力,目前Azure IoTHub已经支持多种操作系统设备的接入,包括And ...

  4. Apache许可翻译

    https://www.apache.org/licenses/LICENSE-2.0 Apache许可 2.0 2004.1 使用.复制和发行的术语和条件. 1 定义 "License&q ...

  5. 深度解析PHP数组函数array_slice

    看到array_slice()这个函数让我想起了VFP中的range这个范围取值的子句 这个函数一共有四个参数: 被取值的数组(必需) 取值的起始位置(必需) 取值的终止位置,如果不填写默认到数组最后 ...

  6. python 收录集中实现线程池的方法

    概念: 什么是线程池? 诸如web服务器.数据库服务器.文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务.构建服务器应用程序的一个过于简单的模型是:每当一个请求到达就创 ...

  7. 矢量量化(VQ)

    作者:桂. 时间:2017-05-31  21:14:56 链接:http://www.cnblogs.com/xingshansi/p/6925955.html 前言 VQ(Vector Quant ...

  8. 香港科技大学的VINS_MONO初试

    简介 VINS-Mono 是香港科技大学开源的一个VIO,我简单的测试了,发现效果不错.做个简单的笔记,详细的内容等我毕设搞完再弄. 代码主要分为前端(feature tracker),后端(slid ...

  9. 如何在vuejs中抽出公共代码

    当我们在使用vue构建中大型项目时,通常会遇到某些经常用的方法以及属性,比如说搭建一个员工管理系统,请求的url需要一个共同的前缀,或者在某几个view中需要用到时间,这个时间是通过某方法格式化之后的 ...

  10. 抽象工厂(AbstractFactory)模式-创建型模式

    1.new 的问题 常见的对象创建方法: //创建一个Road对象 Road road=new Road(); new的问题:实现依赖,不能应对具体实例的变化 怎么理解上面这句话呢? 可以这样理解:我 ...