zoukankan      html  css  js  c++  java
  • java多线程

    概述:

    几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程。当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程。

    进程与线程:

    进程是指处于运行过程中的程序,并且具有一定的独立功能。进程是系统进行资源分配和调度的一个单位。当程序进入内存运行时,即为线程。

    进程拥有以下三个特点:

    1:独立性:进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的地址空间,没有进程本身的运行,用户进程不可以直接访问其他进程的地址空间。

    2:动态性:进程和程序的区别在于进程是动态的,进程中有时间的概念,进程具有自己的生命周期和各种不同的状态。

    3:并发性:多个进程可以在单个处理器上并发执行,互不影响。

    并发性和并行性是不同的概念:并行是指同一时刻,多个命令在多个处理器上同时执行;并发是指在同一时刻,只有一条命令是在处理器上执行的,但多个进程命令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。

    对于一个CPU而言:只能在某一时间点执行一个程序。

    多进程的并发策略有:共用式的多任务操作策略(WIN3.1和Mac OS9),现在操作系统大多采用效率更高的抢占式多任务操作策略(Windows NT、Windows 2000以及UNIX/Linux)等操作系统。

    多线程扩展了多进程了概念,使得同一个进程可以同时并发处理多个任务。
    线程(Thread)被称为轻量级线程(LightWeight Process),线程是进程的执行单元。
     
    线程是进程的组成部分,一个进程可以拥有多个线程,而一个线程必须拥有一个父进程。线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不能拥有系统资源。它与父进程的其他线程共享该进程的所有资源。
     
    线程可以完成一定任务,可以和其它线程共享父进程的共享变量和部分环境,相互协作来完成任务。
    线程是独立运行的,其不知道进程中是否还有其他线程存在。
    线程的执行是抢占式的,也就是说,当前执行的线程随时可能被挂起,以便运行另一个线程。
    一个线程可以创建或撤销另一个线程,一个进程中的多个线程可以并发执行。
     
    线程的创建和启用:
    java使用Thread类代表线程,所有的线程对象都必须是Thread或者其子类的实例,每个线程的作用是完成一定任务,实际上是就是执行一段程序流(一段顺序执行的代码)
    继承Thread类创建线类
    1:定义Thread类的子类 并重写该类的Run方法 该run方法的方法体就代表了线程需要完成的任务
    2:创建Thread类的实例,即创建了线程对象
    3:调用线程的start方法来启动线程
     
    例:
    复制代码
    package Test;
    
    public class FirstThread extends Thread{
        private int i;
    @Override
    public void run() {
        for(;i<10;i++)
        {
            System.out.println(getName()+"	"+i);
        }
    }
    public static void main(String[] args)
    {
        for (int i = 0; i <10; i++) {
          System.out.println(Thread.currentThread().getName()+"	"+i);    
          if(i==5)
            {
                FirstThread f1=new FirstThread();
                FirstThread f2=new FirstThread();
                f1.start();
                f2.start();
            }
        }
        
    }
    }
    复制代码

    Thread.currentThread():总是返回正在执行的线程对象
    getName()返回当前正在执行的线程名
     
    使用继承子Thread类的子类来创建线程类时,多个线程无法共享线程类的实例变量(比如上面的i)
     
     
    使用Runnable接口创建线程类
    1:定义Runnable接口的实现类,并重写它的Run方法,run方法同样是该线程的执行体!
    2:创建Runnable实现类的实例,并将此实例作为Thread的target创建一个Thread对象,该Thread对象才是真正的线程对象!
    3:调用start方法启动该线程
    Runnable对象仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程的对象依旧是Thread实例,只是线程实例负责执行其target的run()方法
    复制代码
    package Test;
    
    public class SecondThread implements Runnable {
        private int i;
        @Override
        public void run() {
            for (; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+" "+i);
                if(i==20)
                {
                    System.out.println(Thread.currentThread().getName()+"执行完毕");
                }
            }
        }
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+" "+i);
                if(i==5)
                {
                    SecondThread s1=new SecondThread();
                    Thread t1=new Thread(s1,"线程1");
                    Thread t2=new Thread(s1,"线程2");
                    t1.start();
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    t2.start();
                }
            }
        }
    
    }
    复制代码

    由于线程的不稳定性,可能同时出现线程1和线程2

    采用Ruunable接口的方式创建多个线程可以共享线程类的实例变量,这是因为在这种方式下,程序创建的Runnable对象只是线程的target,而多个线程可以共享一个target,所以多个线程可以共享一个实例变量!

    使用callable和future创建线程

    通过Runnable实现多线程其实就是将run包装成线程的执行体,但是目前java无法将任意方法包装成线程执行体

    从Java5开始,Java提供 Callable接口,Callable接口提供了一个call()方法可以作为线程执行体,看起来和Runnable很像,但call()方法更强大——call()方法可以有返回值、call()方法可以抛出异常

    Java5提供了Future接口来代表Callable接口的call()方法的返回值,并未Future接口提供了一个FutureTask实现类,该实现类实现类Future接口,也实现了Runnable接口——可以作为Thread的target。

    使用该方法创建有返回值的线程的步骤如下:

    1:创建Callable接口的实现类,并实现call方法,该call方法会成为线程执行体,且call方法具有返回值,在创建callable接口的实现类!

    2:使用FutrueTask类来包装Callable对象,该FutrueTask封装类Callable的call方法的返回值

    3:使用FutrueTask对象作为Thread的target创建并启动新线程!

    4:使用FutrueTask的get方法获取执行结束后的返回值

    复制代码
    package Test;
    
    import java.util.concurrent.Callable;
    
    public class target implements Callable<Integer> {
        int i=0;
        @Override
        public Integer call() throws Exception {
            for (; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+""+i);
            }
            return i;
        }
    
    }
    package Test;
    
    import java.util.concurrent.FutureTask;
    
    public class ThridThread {
    
        public static void main(String[] args) {
            target t1=new target();
            FutureTask<Integer> ft=new FutureTask<Integer>(t1);
            Thread t2=new Thread(ft,"新线程");
            t2.start();
            try {
                System.out.println(ft.get());
            } catch (Exception e) {
                // TODO: handle exception
            }
    
        }
    
    }
    复制代码

    采取Runnable、Callable的优势在于——线程类只是实现了Runnable或Callable接口,还可以继承其它类;在这种方法下,多个线程可以共享一个target对象,因此非常适合多个相同线程处理同一份资源的情况,从而将CPU、代码和数据分开,形参清晰的模型,体现了面对对象的编程思想。劣势在于编程复杂度略高。

  • 相关阅读:
    Fiddler工具抓包
    简单Ant打包
    android中ActionBar的几个属性
    yum安装php7.2
    java 获取计算机内存
    docker apache安装
    docker 安装 openresty
    docker 安装nginx
    docker安装gitlab
    java获取时间格式
  • 原文地址:https://www.cnblogs.com/ruixinyu/p/5975091.html
Copyright © 2011-2022 走看看