精华内容
下载资源
问答
  • Java多线程技术~线程的定义与同步

    万次阅读 热门讨论 2020-06-16 11:38:33
    Java多线程技术 线程和进程 程序Program        程序是一段静态代码,它是应用程序执行蓝本 进程Process        进程是指一种正在运行...

    Java多线程技术

    线程和进程

    程序Program

           程序是一段静态的代码,它是应用程序执行的蓝本

    进程Process

           进程是指一种正在运行的程序,有自己的地址空间。

    在这里插入图片描述

    进程的特点

           动态性

           并发性

           独立性

           并发和并行的区别

                  并行:多个CPU执行多个CPU任务

                  并发:一个CPU(采用时间片)同时执行多个任务

    线程Thread

           进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。

           线程又被称为轻量级进程(lightweight process)

           如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为多线程

    线程特点

           轻量级进程

           独立调度的基本单位

           共享进程资源

           可并发执行

    线程和进程的区别

    区别 进程 线程
    根本区别 作为资源分配的单位 调度和执行的单位
    开 销 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。 线程可以看成时轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
    所处环境 在操作系统中能同时运行多个任务(程序) 在同一应用程序中有多个顺序流同时执行
    分配内存 系统在运行的时候会为每个进程分配不同的内存区域 除了CPU外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源
    包含关系 没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。 线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。

    线程定义和创建

    使用多线程实现赛跑

    public class Test{
        public static void main(String[] args) {
            //创建一个线程
            Student student = new Student();
            //启动一个线程
            //thread.run();这不是在启动线程,是在调用方法run()
            //启动线程,不见得立刻执行,而是进入就绪队列,等待获得CPU
            student.start();
            //小刚也在跑
            for(int i = 0; i < 100; i++){
                System.out.println("小刚现在领先了,加油!    当前线程的名称: " + Thread.currentThread().getName());
            }
        }
    }
    
    class Student extends Thread{
        public void run(){
            for(int i = 0; i < 100; i++){
                System.out.println("小明现在领先了,加油!   当前线程的名称: " + this.getName());
            }
        }
    }
    

           run() 线程体,线程要完成的任务

           start() 启动线程,线程进入就绪队列,等待获取CPU并执行

    实现Runnable接口

    public class Test{
        public static void main(String[] args) {
            //创建线程对象
            Runnable runnable = new Student();
            Thread thread = new Thread(runnable);
            //启动线程
            thread.start();
    
            for(int i = 0; i < 100; i++){
                System.out.println("小刚领先了,加油   当前线程的名称:" + Thread.currentThread().getName());
            }
        }
    }
    
    class Student implements Runnable{
    
        @Override
        public void run() {
            for(int i = 0; i < 100; i++){
                System.out.println("小明领先了,加油!   当前线程的名称:" + Thread.currentThread().getName());
            }
        }
    }
    

    两种方式的优缺点

           方式1:继承Thread类

           缺点:Java单继承,无法继承其他类

           优点:代码稍微简单

           方式2:实现Runnable接口

           优点 还可以去继承其他类 便于多个线程共享同一个资源

           缺点:代码略有繁琐

           实际开发中,Runnable使用更多一些

    方法介绍

           currentThread()

                  返回对当前正在执行的线程对象的引用。

           getName()

                  返回该线程的名称

           getPriority()

                  返回线程的优先级。

           run()

                  果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

           setName(String name)

                  改变线程名称,使之与参数 name 相同。

           setPriority(int newPriority)

                  更改线程的优先级。

           start()

                  使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

    Callable接口

           JDK1.5后推出了第三种定义线程的方式,实现Callable接口

    使用多线程获取随机数
    public class Test implements Callable<Integer> {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //创建线程对象
            Callable<Integer> callable = new Test();
            FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
            Thread thread = new Thread(futureTask);
            //启动线程
            thread.start();
            //获取返回值
            System.out.println(futureTask.isDone());
            //必须等线程执行完毕后,才能得到返回值,线程在此会阻塞
            Integer num = futureTask.get();
            System.out.println(num);
            System.out.println(futureTask.isDone());
        }
    
        @Override
        public Integer call() throws Exception {
            Thread.sleep(500);
            return new Random().nextInt(10);
        }
    }
    

    在这里插入图片描述

    与Runnable相比, Callable功能更强大些

           方法名不同

           可以有返回值,支持泛型的返回值

           可以抛出检查异常

           需要借助FutureTask,比如获取返回结果

    Future接口

           可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等

           FutrueTask是Futrue接口的唯一的实现类

           FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

    在这里插入图片描述

    新生状态

           用new关键字建立一个线程对象后,该线程对象就处于新生状态。

           处于新生状态的线程有自己的内存空间,通过调用start进入就绪状态

    就绪状态

           处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU

           当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称之为“cpu调度”

    运行状态

           在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任务而死亡。

           如果在给定时间片内没执行结束,就会被系统给换下来回到等待执行状态。

    阻塞状态

           处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。

           在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。

    死亡状态

           死亡状态是线程生命周期中最后一个阶段。线程死亡原因有三个。一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过执行stop方法来终止一个线程[不推荐使用】,三是线程抛出未捕获的异常

    线程控制

           理解了线程生命周期的基础上,可以使用Java提供的线程控制命令对线程的生命周期进行干预。

           实际开发中经常使用Thread.sleep()来模拟线程切换,暴露线程安全问题。

    join ()

           阻塞指定线程等到另一个线程完成以后再继续执行

    public class Test {
        public static void main(String[] args){
            int j = 1;
            for(int i = 0; i < 100; i++){
                if(i == 50){
                    Thread thread = new Student();
                    thread.setName("关云长");
                    thread.start();
                    try {
                        // 线程A正在执行 线程B进来,线程B执行完,A才会执行。
                        //A此间处于阻塞状态
                        thread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("小刚领先了,加油!   当前线程名: " + Thread.currentThread().getName() + j++);
            }
        }
    }
    
    class Student extends Thread{
        int j = 1;
        public void run(){
            for(int i = 0; i < 100; i++){
                System.out.println("小明领先了,加油!   当前线程名: " + this.getName() + j++);
            }
        }
    }
    

    sleep()

           使线程停止运行一段时间,让出CPU,将处于阻塞状态如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行!

    public class Test {
        public static void main(String[] args){
            Thread thread = new Student();
            thread.start();
            for(int i = 0; i < 100; i++){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("小刚领先了,加油!   当前线程名: " + Thread.currentThread().getName());
            }
        }
    }
    
    class Student extends Thread{
        public void run(){
            for(int i = 0; i < 100; i++){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("小明领先了,加油!   当前线程名: " + this.getName());
            }
        }
    }
    

    setDaemon()

           可以将指定的线程设置成后台线程,创建后台线程的线程结束时,后台线程也随之消亡,只能在线程启动之前把它设为后台线程

    public static void main(String[] args){
            Thread thread = new Student();
            thread.setDaemon(true); //后台线程 
            thread.start();
            for(int i = 0; i < 100; i++){
                System.out.println("小刚领先了,加油!   当前线程名: " + Thread.currentThread().getName());
            }
        }
    

    interrupt()

           并没有直接中断线程,而是需要被中断线程自己处理

    stop()

           结束线程,不推荐使用。

    yield

           让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态,如果调用了yield方法之后,没有其他等待执行的线程,这个时候当前线程就会马上恢复执行!


    线程同步

    线程同步的实现方案

           同步代码块

                  synchronized(obj){}

           同步方式

                  private synchronized void makeWithdrawal(int amt) {}

           Lock锁

    售票案例(Synchronized)

    public class Test {
        public static void main(String[] args){
            Ticket ticket = new Ticket();
            //创建Thread类
            Thread A = new Thread(ticket,"A窗口");
            Thread B = new Thread(ticket,"B窗口");
            Thread C = new Thread(ticket,"C窗口");
            //启动线程
            A.start();
            B.start();
            C.start();
        }
    }
    class Ticket implements Runnable{
        private int ticket = 5;
    
        @Override
        public void run() {
            for(int i = 0; i < 100; i++){   //每个窗口排了100个人
                synchronized (this){
                    if(ticket > 0){ //有票
                        try {
                            Thread.sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket-- + " 张票");
                    }
                }
            }
        }
    }
    

    在这里插入图片描述

           这个时候我们会看到票是一张一张的来卖的

           假如没有锁呢?

    在这里插入图片描述

           那么就会出现这种情况

    认识同步监视器(锁子)

           synchronized(同步监视器){ }

                  必须是引用数据类型,不能是基本数据类型

                  在同步代码块中可以改变同步监视器对象的值,不能改变其引用

                  尽量不要String和包装类Integer做同步监视器.如果使用了,只要保证代码块中不对其进行任何操作也没有关系

                  一般使用共享资源做同步监视器即可

                  也可以创建一个专门的同步监视器,没有任何业务含义

                  建议使用final修饰同步监视器

           同步代码块的执行过程

                  第一个线程来到同步代码块,发现同步监视器open状态,需要close,然后执行其中的代码

                  第一个线程执行过程中,发生了线程切换(阻塞 就绪),第一个线程失去了cpu,但是没有开锁open

                  第二个线程获取了cpu,来到了同步代码块,发现同步监视器close状态,无法执行其中的代码,第二个线程也进入阻塞状态

                  第一个线程再次获取CPU,接着执行后续的代码;同步代码块执行完毕,释放锁open

                  第二个线程也再次获取cpu,来到了同步代码块,发现同步监视器open状态,重复第一个线程的处理过程(加锁)

           线程同步优点是比较安全,缺点就是效率低下,可能出现死锁。

           多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块,其他线程无法访问其中的任何一个代码块,多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块, 但是没有锁住使用其他同步监视器的代码块,其他线程有机会访问其他同步监视器的代码块

    Lock锁

           JDK1.5中推出了新一代的线程同步方式:Lock锁

    class Ticket implements Runnable{
        private int ticket = 5;
        private Lock lock = new ReentrantLock();    //可重入锁
        @Override
        public void run() {
            for(int i = 0; i < 100; i++){   //每个窗口排了100个人
                try{
                    //上锁
                    lock.lock();
                    if(ticket > 0){ //有票
                        try {
                            Thread.sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket-- + " 张票");
                    }
                }finally {
                    //解锁
                    lock.unlock();
                }
            }
        }
    }
    

    Lock锁

           JDK1.5后新增功能,与采用synchronized相比,lock可提供多种锁方案,更灵活

           java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。

           ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义, 但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。

           注意:如果同步代码有异常,要将unlock()写入finally语句块

    Lock和synchronized的区别

           Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,遇到异常自动解锁

           Lock只有代码块锁,synchronized有代码块锁和方法锁

           Lock锁可以对读不加锁,对写加锁,synchronized不可以

           Lock锁可以有多种获取锁的方式,可以从sleep的线程中抢到锁,synchronized不可以

           使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

    优先使用顺序:

           Lock----同步代码块(已经进入了方法体,分配了相应资源)----同步方法(在方法体之外)

    展开全文
  • 线程线程是进程中一个实体,作为系统调度和分派基本单位。Linux下的线程看作轻量级进程。 图解定义: 进程三态模型: (1)运行:当一个进程在处理机上运行时,则称该进程处于运行状态。处于此状态...

    一、定义

    进程:指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。

    线程:线程是进程中的一个实体,作为系统调度和分派的基本单位。Linux下的线程看作轻量级进程。

    图解定义:

    进程的三态模型:

    (1)运行:当一个进程在处理机上运行时,则称该进程处于运行状态。处于此状态的进程的数目小于等于处理器的数目,对于单处理机系统,处于运行状态的进程只有一个。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
    (2)就绪:当一个进程获得了除处理机以外的一切所需资源,一旦得到处理机即可运行,则称此进程处于就绪状态。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
    (3)阻塞:也称为等待或睡眠状态,一个进程正在等待某一事件发生(例如请求I/O而等待I/O完成等)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称该进程处于阻塞状态。

    进程的五态模型:

    (4)新建态:对应于进程刚刚被创建时没有被提交的状态,并等待系统完成创建进程的所有必要信息。 进程正在创建过程中,还不能运行。操作系统在创建状态要进行的工作包括分配和建立进程控制块表项、建立资源表格(如打开文件表)并分配资源、加载程序并建立地址空间表等。创建进程时分为两个阶段,第一个阶段为一个新进程创建必要的管理信息,第二个阶段让该进程进入就绪状态。由于有了新建态,操作系统往往可以根据系统的性能和主存容量的限制推迟新建态进程的提交。
    (5)终止态:进程已结束运行,回收除进程控制块之外的其他资源,并让其他进程从进程控制块中收集有关信息(如记帐和将退出代码传递给父进程)。类似的,进程的终止也可分为两个阶段,第一个阶段等待操作系统进行善后处理,第二个阶段释放主存。 

    二、抽象理解

    线程:如果把线程比喻为一条线,那么每句代码就好像一个点,线程的工作就是从开始的点走到结束的点。

    多线程:就好像是有多条线,多条线"同时走"(宏观上),有的先开始,有的后开始,有的先结束,有的后结束。

    进程:可以看做是由一条线或者更多条线组成的平面图形,所有的线程都有一个自己的进程,也可能多个线程拥有同一个进程。

    多进程:由多个平面图形组成的立体图形

    注:单CPU时"同时走"按照操作系统理论严格来说,多线程并不是多个线程一起运行的,只是运行线程的切换极快,几乎可以看成是并行运行,也可以说同时运行,但实际上并不是同时的。多核或者多CPU是可能达到"同时走"

    三、事例说明

    多线程:迅雷下载的任务能够多个一起下载,而不是一个下载完成后才开始下载第二个,或者说在浏览器中能一边听音乐一边浏览网页

    多进程:同时执行多个程序,如同时运行Word编辑器,QQ等程序

    四、并发,并行

    并行:当有多个CPU或者是多核CPU时才有可能实现并行,并行就是多个线程或者多个进程同时运行

    并发:单个CPU(也可以多个CPU)将多个线程中的每个线程(多个进程中的每个进程)按时间分为一个一个的时间片,每一个时刻只执行某个线程(进程)的时间片,时间片过期后转而执行下一个线程(进程)的时间片

    注:并发宏观上看起来像是并行但是微观上并不能做到并行

     

    版权声明:本博客为记录本人自学感悟,内容大多从网上学习与整理所得,若侵权请告知!

    https://mp.csdn.net/postedit/80365677

     

    展开全文
  • 线程的定义

    千次阅读 2015-05-18 15:55:20
    线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流最小单元。一个标准的线程线程ID,当前指令指针(PC),寄存器集合和堆栈组成。...一个线程可以创建和撤消另一个线程,同一进程中的多
    线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪阻塞运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
    线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程

    多线程OS中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。
    1)轻型实体
    线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。
    线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。TCB包括以下信息:
    (1)线程状态。
    (2)当线程不运行时,被保存的现场资源。
    (3)一组执行堆栈。
    (4)存放每个线程的局部变量主存区。
    (5)访问同一个进程中的主存和其它资源。
    用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器堆栈
    2)独立调度和分派的基本单位。
    在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
    3)可并发执行。
    在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。
    4)共享进程资源。
    线程线程
    在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存文件,所以线程之间互相通信不必调用内核

    4与进程比较

    线程线程
    进程是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块PCB中。以表示该进程拥有这些资源或正在使用它们。
    另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。
    与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。
    线程只由相关堆栈系统栈或用户栈寄存器和线程控制表TCB组成。寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量。
    通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。因而近年来推出的通用操作系统都引入了线程,以便进一步提高系统的并发性,并把它视为现代操作系统的一个重要指标。
    线程与进程的区别可以归纳为以下4点:
    1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
    2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
    3)调度和切换:线程上下文切换比进程上下文切换要快得多。
    4)在多线程OS中,进程不是一个可执行的实体。

    5守护线程

    守护线程是特殊的线程,一般用于在后台为其他线程提供服务.
    Java中,isDaemon():判断一个线程是否为守护线程.
    Java中,setDaemon():设置一个线程为守护线程.
    类1:守护线程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    /**
    * 本线程设置了一个超时时间
    * 该线程开始运行后,经过指定超时时间,
    * 该线程会抛出一个未检查异常通知调用该线程的程序超时
    * 在超时结束前可以调用该类的cancel方法取消计时
    * @author solonote
    */
     
    public class TimeoutThread extends Thread{
    /**
    * 计时器超时时间
    */
    private long timeout;
    /**
    * 计时是否被取消
    */
    private boolean isCanceled = false;
    /**
    * 当计时器超时时抛出的异常
    */
    private TimeoutException timeoutException;
    /**
    * 构造器
    * @param timeout 指定超时的时间
    */
    public TimeoutThread(long timeout,TimeoutException timeoutErr) {
    super();
    this.timeout = timeout;
    this.timeoutException = timeoutErr;
    //设置本线程为守护线程
    this.setDaemon(true);
    }
    /**
    * 取消计时
    */
    public synchronized void cancel()
    {
    isCanceled = true;
    }
    /**
    * 启动超时计时器
    */
    public void run()
    {
    try {
    Thread.sleep(timeout);
    if(!isCanceled)
    throw timeoutException;
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }

    6线程的同步

    线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?对于同步,在具体的Java代码中需要完成以下两个操作:把竞争访问的资源标识为private;同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。当然这不是唯一控制并发安全的途径。synchronized关键字使用说明synchronized只能标记非抽象的方法,不能标识成员变量。为了演示同步方法的使用,构建了一个信用卡账户,起初信用额为100w,然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步,并将账户的余额设为私有变量,禁止直接访问。
    工作原理
    线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。线程可以创建和撤消线程,从而实现程序的并发执行。一般,线程具有就绪阻塞运行三种基本状态。
    在多中央处理器系统里,不同线程可以同时在不同的中央处理器上运行,甚至当它们属于同一个进程时也是如此。大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的线程与各处理器之间的关联度(affinity)。
    有时候,线程也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存文件句柄和其它每个进程应有的状态。
    进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量对象,而且它们从同一堆中分配对象。尽管这让线程之间共享信息变得更容易,但您必须小心,确保它们不会妨碍同一进程里的其它线程。
    Java 线程工具和 API看似简单。但是,编写有效使用线程的复杂程序并不十分容易。因为有多个线程共存在相同的内存空间中并共享相同的变量,所以您必须小心,确保您的线程不会互相干扰。
    线程属性
    为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。必须知道如何提供线程体、线程的生命周期、实时系统如 何调度线程、线程组、什么是幽灵线程(Demo nThread)。
    线程体
    所有的操作都发生在线程体中,在Java中线程体是从Thread继承的run()方法,或实现Runnable接口的类中的run()方法。当线程产生并初始化后,实时系统调用它的run()方法。run()方法内的代码实现所产生线程的行为,它是线程的主要部分。
    线程状态
    线程的状态线程的状态
    附图表示了线程在它的生命周期内的任何时刻所能处的状态以及引起状态改变的方法。这图并不是完整的有限状态图,但基本概括了线程中比较感兴趣和普遍的方面。以下讨论有关线程生命周期以此为据。
    ●新线程态(New Thread)
    产生一个Thread对象就生成一个新线程。当线程处于"新线程"状态时,仅仅是一个空线程对象,它还没有分配到系统资源。因此只能启动或终止它。任何其他操作都会引发异常。例如,一个线程调用了new方法之后,并在调用start方法之前的处于新线程状态,可以调用start和stop方法。
    ●可运行态(Runnable)
    start()方法产生运行线程所必须的资源,调度线程执行,并且调用线程的run()方法。在这时
    线程的生命状态与周期线程的生命状态与周期
    线程处于可运行态。该状态不称为运行态是因为这时的线程并不总是一直占用处理机。特别是对于只有一个处理机的PC而言,任何时刻只能有一个处于可运行态的线程占用处理 机。Java通过调度来实现多线程处理机的共享。注意,如果线程处于Runnable状态,它也有可能不在运行,这是因为还有优先级和调度问题。
    阻塞/非运行态(Not Runnable)
    当以下事件发生时,线程进入非运行态。
    线程线程
    ①suspend()方法被调用;
    ②sleep()方法被调用;
    ③线程使用wait()来等待条件变量
    ④线程处于I/O请求的等待。
    ●死亡态(Dead)
    当run()方法返回,或别的线程调用stop()方法,线程进入死亡态。通常Applet使用它的stop()方法来终止它产生的所有线程。
    线程的本操作:
    派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。
    阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。
    激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。
    调度(schedule):选择一个就绪线程进入执行状态
    结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。
    图2 线程的状态与操作
    线程的另一个执行特性是同步。线程中所使用的同步控制机制与进程中所使用的同步控制机制相同。
    线程优先级
    虽然我们说线程是并发运行的。然而事实常常并非如此。正如前面谈到的,当系统中只有一个CPU时,以某种顺序在单CPU情况下执行多线程被称为调度(scheduling)。Java采用的是一种简单、固定的调度法,即固定优先级调度。这种算法是根据处于可运行态线程的相对优先级来实行调度。当线程产生时,它继承原线程的优先级。在需要时可对优先级进行修改。在任何时刻,如果有多条线程等待运行系统选择优先级最高的可运行线程运行。只有当它停止、自动放弃、或由于某种原因成为非运行态低优先级的线程才能运行。如果两个线程具有相同的优先级,它们将被交替地运行。 Java实时系统的线程调度算法还是强制性的,在任何时刻,如果一个比其他线程优先级都高的线程的状态变为可运行态,实时系统将选择该线程来运行。一个应用程序可以通过使用线程中的方法setPriority(int),来设置线程的优先级大小。
    线程进入了就绪状态,需要有线程调度程序来决定何时执行,根据优先级来调度。
    线程中的join()可以用来邀请其他线程先执行(示例代码如下):
    packageorg.thread.test;publicclassJoin01implementsRunnable{publicstaticvoidmain(String[]args){for(inti=0;i<20;i++){if(i==5){Join01j=newJoin01();Threadt=newThread(j);t.setName("被邀请先执行的线程.");t.start();try{//邀请这个线程,先执行t.join();}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println("没被邀请的线程。"+(i+1));}}publicvoidrun(){for(inti=0;i<10;i++){System.out.println(Thread.currentThread().getName()+(i+1));}}}
    yield()告诉系统"把自己的CPU时间让掉,让其他线程或者自己运行",示例代码如下:
    packageorg.thread.test;
    publicclassYield01
    {
    publicstaticvoidmain(String[]args)
    {
    YieldFirstyf=newYieldFirst();
    YieldSecondys=newYieldSecond();
    YieldThirdyt=newYieldThird();
    yf.start();ys.start();yt.start();
    }
    }
    classYieldFirstextendsThread
    {
    @Overridepublicvoidrun()
    {
    for(inti=0;i<10;i++)
    {
    System.out.println("第一个线程第"+(i+1)+"次运行.");//让当前线程暂停yield();
    }
    }
    }
    classYieldSecondextendsThread
    {
    @Overridepublicvoidrun()
    {
    for(inti=0;i<10;i++)
    {
    System.out.println("第二个线程第"+(i+1)+"次运行.");//让当前线程暂停yield();
    <a href="mailto:}}}classYieldThirdextendsThread{@Overridepublicvoidrun(){for(inti=0;i}
    }
    }
    classYieldThirdextendsThread
    {
    @Overridepublicvoidrun(){for(inti=0;i<10;i++)
    {
    System.out.println("第三个线程第"+(i+1)+"次运行.");//让当前线程暂停yield();
    }
    }
    幽灵线程
    任何一个Java线程都能成为幽灵线程。它是作为运行于同一个进程内的对象和线程的服务提供者。例如,HotJava浏览器有一个称为" 后台图片阅读器"的幽灵线程,它为需要图片的对象和线程从文件系统或网络读入图片。 幽灵线程是应用中典型的独立线程。它为同一应用中的其他对象和线程提供服务。幽灵线程的run()方法一般都是无限循环,等待服务请求。
    线程组
    每个Java线程都是某个线程组的成员。线程组提供一种机制,使得多个线程集于一个对象内,能对它们实行整体操作。譬如,你能用一个方法调用来启动或挂起组内的所有线程。Java线程组由ThreadGroup类实现。
    当线程产生时,可以指定线程组或由实时系统将其放入某个缺省的线程组内。线程只能属于一个线程组,并且当线程产生后不能改变它所属的线程组。
    多线程
    多线程多线程
    对于多线程的好处这就不多说了。但是,它同样也带来了某些新的麻烦。只要在设计程序时特别小心留意,克服这些麻烦并不算太困难。在生成线程时必须将线程放在指定的线程组,也可以放在缺省的线程组中,缺省的就是生成该线程的线程所在的线程组。一旦一个线程加入了某个线程组,不能被移出这个组。
    同步线程
    多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态。这就需要同步机制。在Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。它由高层的结构隐式实现,来保证操作的对应。(然而,我们注意到Java虚拟机提供单独的monito renter和monitorexit指令来实现lock和
    线程同步线程同步
    unlock操作。) synchronized语句计算一个对象引用,试图对该对象完成锁操作,并且在完成锁操作前停止处理。当锁操作完成synchronized语句体得到执行。当语句体执行完毕(无论正常或异常),解锁操作自动完成。作为面向对象的语言,synchronized经常与方法连用。一种比较好的办法是,如果某个变量由一个线程赋值并由别的线程引用或赋值,那么所有对该变量的访问都必须在某个synchromized语句或synchronized方法内。
    现在假设一种情况:线程1与线程2都要访问某个数据区,并且要求线程1的访问先于线程2,则这时仅用synchronized是不能解决问题的。这在Unix或Windows NT中可用Simaphore来实现。而Java并不提供。在Java中提供的是wait()和notify()机制。使用如下:
    synchronizedmethod_1(/*……*/){//calledbythread1.//accessdataareaavailable=true;notify();}synchronizedmethod_2(/*……*/){//calledbythread2.while(!available)try{wait();//waitfornotify().}catch(InterruptedExceptione){}//accessdataarea}
    其中available是类成员变量,置初值为false。
    如果在method-2中检查available为假,则调用wait()。wait()的作用是使线程2进入非运行态,并且解锁。在这种情况下,method-1可以被线程1调用。当执行notify()后。线程2由非运行态转变为可运行态。当method-1调用返回后。线程2可重新对该对象加锁,加锁成功后执行wait()返回后的指令。这种机制也能适用于其他更复杂的情况。
    死锁
    如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源。系统中没有饿死和死锁的线程。Java并不提供对死锁的检测机制。对大多数的Java程序员来说防止死锁是一种较好的选择。最简单的防止死锁的方法是对竞争的资源引入序号,如果一个线程需要几个资源,那么它必须先得到小序号的资源,再申请大序号的资源。
    优化
    Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意。原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操作系统互斥(mutex)来实现的。而互斥是一种会导致线程挂起,并在较短的时间内又需要重新调度回原线程的,较为消耗资源的操作。所以需要进行对线程进行优化,提高效率。
    轻量级锁
    轻量级锁(Lightweight Locking)是从Java6开始引入的概念,本意是为了减少多线程进入互斥的几率,并不是要替代互斥。它利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG),尝试在进入互斥前,进行补救。下面将详细介绍JVM如何利用CAS,实现轻量级锁。
    mark word结构mark word结构
    Java Object Model中定义,Object Header是一个2字(1 word = 4 byte)长度的存储区域。第一个字长度的区域用来标记同步,GC以及hash code等,官方称之为 mark word。第二个字长度的区域是指向到对象的Class。在2个word中,mark word是轻量级锁实现的关键,其结构见右表。
    联系流程图联系流程图
    从表中可以看到,state为lightweight locked的那行即为轻量级锁标记。bitfieds名为指向lock record的指针,这里的lock record,其实是一块分配在线程堆栈上的空间区域。用于CAS前,拷贝object上的mark word。第三项是重量级锁标记。后面的状态单词很有趣,inflated,译为膨胀,在这里意思其实是锁已升级到OS-level。一般我们只关注第二和第三项即可。lock,unlock与mark word之间的联系如右图所示。在图中,提到了拷贝object mark word,由于脱离了原始mark word,官方将它冠以displaced前缀,即displaced mark word(置换标记字)。这个displaced mark word是整个轻量级锁实现的关键,在CAS中的compare就需要用它作为条件。
    交换指针交换指针
    交换指针交换指针
    在拷贝完object mark word之后,JVM做了一步交换指针的操作,即流程中第一个橙色矩形框内容所述。将object mark word里的轻量级锁指针指向lock record所在的stack指针,作用是让其他线程知道,该object monitor已被占用。lock record里的owner指针指向object mark word的作用是为了在接下里的运行过程中,识别哪个对象被锁住了。
    最后一步unlock中,我们发现,JVM同样使用了CAS来验证object mark word在持有锁到释放锁之间,有无被其他线程访问。如果其他线程在持有锁这段时间里,尝试获取过锁,则可能自身被挂起,而mark word的重量级锁指针也会被相应修改。此时,unlock后就需要唤醒被挂起的线程。
    偏向锁
    Java偏向锁(Biased Locking)是Java 6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。它与轻量级锁的区别在于,轻量级锁是通过CAS来避免进入开销较大的互斥操作,而偏向锁是在无竞争场景下完全消除同步,连CAS也不执行(CAS本身仍旧是一种操作系统同步原语,始终要在JVM与OS之间来回,有一定的开销)。所谓的无竞争场景,就是单线程访问带同步的资源或方法。
    偏向锁操作流程偏向锁操作流程
    偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在接下来的运行过程中,该锁没有被其他的线程访问,则持有偏向锁的线程将永远不需要触发同步。如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会尝试消除它身上的偏向锁,将锁恢复到标准的轻量级锁。(偏向锁只能在单线程下起作用)。
    偏向模式和非偏向模式,在mark word表中,主要体现在thread ID字段是否为空。
    挂起持有偏向锁的线程,这步操作类似GC的pause,但不同之处是,它只挂起持有偏向锁的线程(非当前线程)。
    在抢占模式的橙色区域说明中有提到,指向当前堆栈中最近的一个lock record(在轻量级锁中,lock record是进入锁前会在stack上创建的一份内存空间)。这里提到的最近的一个lock record,其实就是当前锁所在的stack frame上分配的lock record。整个步骤是从偏向锁恢复到轻量级锁的过程。
    偏向锁也会带来额外开销。在JDK6中,偏向锁是默认启用的。它提高了单线程访问同步资源的性能。
    但试想一下,如果你的同步资源或代码一直都是多线程访问的,那么消除偏向锁这一步骤对你来说就是多余的。事实上,消除偏向锁的开销还是蛮大的。所以在你非常熟悉自己的代码前提下,大可禁用偏向锁 -XX:-UseBiasedLocking。
    分类
    线程有两个基本类型
    用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。
    系统级线程(核心级线程):由操作系统内核进行管理。操作系统内核给应用程序提供相应的系统调用应用程序接口API,以使用户程序可以创建、执行、撤消线程。
    举例UNIX International 线程
    UNIX International 线程的头文件是<thread.h>[1] ,仅适用于Sun Solaris操作系统。所以UNIX International线程也常被俗称为Solaris线程。
    1.创建线程
    intthr_create(void*stack_base,size_tstack_size,void*(*start_routine)(void*),void*arg,longflags,thread_t*new_thr);
    2.等待线程
    intthr_join(thread_twait_for,thread_t*dead,void**status);
    3.挂起线程
    intthr_suspend(thread_tthr);
    4.继续线程
    intthr_continue(thread_tthr);
    5.退出线程
    voidthr_exit(void*status);
    6.返回当前线程的线程标识符
    thread_tthr_self(void);POSIX线程
    POSIX线程(Pthreads)的头文件是<pthread.h>[2] ,适用于类Unix操作系统。Windows操作系统并没有对POSIX线程提供原生的支持库。不过Win32的POSIX线程库的一些实现也还是有的,例如pthreads-w32[3] 
    1.创建线程
    intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);
    2.等待线程
    intpthread_join(pthread_tthread,void**retval);
    3.退出线程
    voidpthread_exit(void*retval);
    4.返回当前线程的线程标识符
    pthread_tpthread_self(void);
    5.线程取消
    intpthread_cancel(pthread_tthread);Win32线程
    Win32线程的头文件是<Windows.h>,适用于Windows操作系统。
    1.创建线程
    HANDLEWINAPICreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,SIZE_TdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,LPDWORDlpThreadId);
    2.结束本线程
    VOIDWINAPIExitThread(DWORDdwExitCode);
    3.挂起指定的线程
    DWORDWINAPISuspendThread(HANDLEhThread);
    4.恢复指定线程运行
    DWORDWINAPIResumeThread(HANDLEhThread);
    5.等待线程运行完毕
    DWORDWINAPIWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);
    6.返回当前线程的线程标识符
    DWORDWINAPIGetCurrentThreadId(void);
    7.返回当前线程的线程句柄
    HANDLEWINAPIGetCurrentThread(void);C++ 11 线程
    C++ 11 线程的头文件是<thread>。
    1. 创建线程
      std::thread::thread(Function&& f, Args&&... args);[4] 
    2. 等待线程结束
      std::thread::join();[4] 
    3. 脱离线程控制
      std::thread::detach();[4] 
    4. 交换线程
      std::thread::swap( thread& other );[4] 
    C 11 线程
    C11线程的头文件是<threads.h>。
    C11线程仅仅是个“建议标准”,也就是说100%遵守C11标准的C编译器是可以不支持C11线程的。根据C11标准的规定,只要编译器预定义了__STDC_NO_THREADS__宏,就可以没有<threads.h>头文件,自然也就也没有下列函数。[5] 
    1.创建线程
    intthrd_create(thrd_t*thr,thrd_start_tfunc,void*arg);
    2.结束本线程
    _Noreturnvoidthrd_exit(intres);
    3.等待线程运行完毕
    intthrd_join(thrd_tthr,int*res);
    4.返回当前线程的线程标识符
    thrd_tthrd_current();Java线程
    1)最简单的情况是,Thread/Runnable的run()方法运行完毕,自行终止。
    2)对于更复杂的情况,比如有循环,则可以增加终止标记变量和任务终止的检查点。
    3)最常见的情况,也是为了解决阻塞不能执行检查点的问题,用中断来结束线程,但中断只是请求,并不能完全保证线程被终止,需要执行线程协同处理。[6] 
    4)IO阻塞和等锁情况下需要通过特殊方式进行处理。
    5)使用Future类的cancel()方法调用。
    6)调用线程池执行器的shutdown()和shutdownNow()方法。
    7)守护线程会在非守护线程都结束时自动终止。
    8)Thread的stop()方法,但已不推荐使用。
    线程的组成
    1)一组代表处理器状态的CPU寄存器中的内容
    2)两个栈,一个用于当线程在内核模式下执行的时候,另一个用于线程在用户模式下执行的时候
    3)一个被称为线程局部存储器(TLS,thread-local storage)的私有储存区域,各个子系统、运行库和DLL都会用到该储存区域
    4)一个被称为线程ID(thread ID,线程标识符)的唯一标识符(在内部也被称为客户ID——进程ID和线程ID是在同一个名字空间中生产的,所以它们永远 不会重叠)
    5)有时候线程也有它们自己的安全环境,如果多线程服务器应用程序要模仿其客户的安全环境,则往往可以利用线程的安全环境

    7线程状态变化

    (1)创建线程[7] 
    当创建一个新的进程时,也创建一个新的线程,进程中的线程可以在同一进程中创建新的线程中创建新的线程。
    创建线程创建线程
    (2)终止线程
    可以正常终止自己,也可能某个线程执行错误,由其它线程强行终止。终止线程操作主要负责释放线程占有的寄存器和栈
    (3)阻塞线程
    当线程等待每个事件无法运行时,停止其运行。
    阻塞线程阻塞线程
    (4)唤醒线程
    当阻塞线程的事件发生时,将被阻塞的线程状态置为就绪态,将其挂到就绪队列。进程仍然具有与执行相关的状态。例如,所谓进程处于“执行”状态,实际上是指该进程中的某线程正在执行。对进程施加的与进程状态有关的操作,也对其线程起作用。例如,把某个进程挂起时,该进程中的所有线程也都被挂起,激活也是同样。

    展开全文
  • 任务中都会有循环结构(没有循环就不需要多线程了,开多线程就是怕这里循环影响其他),只要控制住循环就可以结束任务 控制循环通常就用定义标记来完成 */ class StopThread implements Runnable{ private ...
    /*
    停止线程:
    1 stop 方法。
    
    2 run 方法结束
    
    怎么控制线程的任务结束?
    
    任务中都会有循环结构(没有循环就不需要多线程了,开多线程就是怕这里循环影响其他),只要控制住循环就可以结束任务
    控制循环通常就用定义标记来完成
    
    	
    */
    class StopThread implements Runnable{
    	private boolean flag=true;
    	public void run(){
    		while(flag){
    			System.out.println(Thread.currentThread().getName()+"....");
    
    		}
    		
    	}
    	public void setFlag(){
    			flag=false;
    		}
    }
    
    
    
    
    class StopThreadDemo{
    	public static void main(String[] args) {
    		StopThread st=new StopThread();
    
    		Thread t1=new Thread(st);
    		Thread t2=new Thread(st);
    
    		t1.start();
    		t2.start();
    
    		int num=1;
    		for(;;){
    			if(++num==50){
    				st.setFlag();//此处将标记变为false 停止线程 
    				break;
    			}
    			System.out.println("main..."+num);
    		}
    
    	}
    }

    展开全文
  • 多线程的特点 * 1)执行的顺序没有规律 * 2)两个线程是同时执行的 *3.lamda的定义方式 * * * */using System;using System.Collections.Generic;using System.Linq;using System.Text;using Syste...
  • 1:进程 定义:是程序在计算机上一次执行活动。Windows系统利用进程把工作...一个正在运行应用程序在操作系统中被视为一个进程,进程可以包括一个或线程。 2:线程 命名空间:using System.Threading 定...
  • 多线程操作系统中,每个线程都是独立,互不干扰,所以要为每个线程分配独立栈空间,这个栈空间通常是一个预先定义全局数组, 也可以是动态分配一段内存空间,但它们都存在于 RAM 中。定义两个线程栈如下:...
  • 手动和自动同时访问全局变量时,就有可能出现线程不同步问题。以下主要利用lock线程锁来修改解决方案,使线程同步,详细代码如下。   using System; using System.Collections.Generic; using System....
  • 线程安全定义

    2019-01-15 10:31:07
    线程安全的定义如下: 当线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全...
  • 定义多线程任务

    2015-03-16 08:20:23
    线程可以驱动任务,因此,我们需要一种描述任务的方式,我们定义多线程任务有两种方式可以实现,一种是继承Thread类,重写run方法,如下所示,我们使用这种方法定义了一个多线程的计数器。 package thread; ...
  • 1. ThreadLocal初始化定义 protected static final ThreadLocal<Set<String>> VERTEX_TABLES_THREAD_LOCAL = ThreadLocal.withInitial(HashSet::new);
  • 1、继承多线程Thread类 再覆盖线程类run()方法
  • 带返回值的CallableJAVA线程相关的Runnable接口中的run()方法没有提供返回值,如下:...... public void run() { ...... } .........如果还需要为线程的执行调度加入更复杂的控制逻辑,那么需要我
  • 线程安全的定义

    2018-09-11 18:32:34
    线程访问一个类时,这个类始终能表现出正确行为,那这个类就是线程安全
  • Java多线程的引入

    2017-03-20 16:56:49
    多线程的定义是:同时对多项任务加以控制。 那什么是多线程呢?这个问题可以理解成一边吃饭、一边听歌。这个是多线程。假如吃完饭再听歌, 或者是先听歌再吃饭,这个是单线程。 程序里同时执行多个任务...
  • 一、定义线程 1、扩展java.lang.Thread类。 此类中有个run()方法,应该注意其用法: public void run() java.lang 类 Thread java.lang.Object  java.lang.Thread 所有已实现接口: Runnable public...
  • 进程和线程的定义及区别一, 进程的概念进程是在道程序系统出现以后,为了描述系统内部各作业的活动规律而引进的概念。由 于多道程序系统所带来的复杂环境,程序本身有了并行性【为了充分利用资源,在主存中同时...
  • 我对多线程的理解和分类

    千次阅读 2016-04-24 22:14:02
    一、多线程的定义和使用信息: 多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径 在系统级别内,程序并排执行,程序分配到每个程序的执行时间是基于该程序的所需时间和其他程序的所需时间来...
  • 多线程并发是一个非常难搞一个问题,不知道多少英雄白头,也没达到最好方法,以前我也是申名 static object o_lock=new object;在线程中采用lock(o_lock) ,这是简单,后来有了ReaderWriterLock,这个类,改变了一点...
  • JAVA线程的定义和使用方法

    千次阅读 2013-10-27 15:28:36
    来源于DreamSea DreamSea的博客 :...线程的概述(Introduction) 线程是一个程序的个执行路径,执行调度的单位,依托于进程存在。 线程不仅可以共享进程的内存,而且还拥有一个属于自
  • 进程-线程-多线程 1、进程(process) 狭义定义:进程就是一段程序执行过程 简单来讲进程概念主要有两点: 第一,进程是一个实体。每一个进程都有它自己地址空间,一般情况下,包括文本区域(text region)...
  • 一、进程与多线程的定义 进程是指程序的一次动态执行。线程是比进程更小的单位,是在进程基础之上进行的进一步划分,也是用来进行动态的执行程序。多线程是指实现并发机制的一种手段。举个例子:你去A餐馆吃饭点了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,489
精华内容 8,595
关键字:

多线程的定义