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

    线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
    进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能



    什么是多线程呢?即就是一个程序中有多个线程在同时执行。
    通过下图来区别单线程程序与多线程程序的不同:
    ? 单线程程序:即,若有多个任务只能依次执行。当上一个任务执行结束后,下一个任务开始执行。如,去网吧上网,网吧只能让一个人上网,当这个人下机后,下一个人才能上网。
    ? 多线程程序:即,若有多个任务可以同时执行。如,去网吧上网,网吧能够让多个人同时上网。

    ? 分时调度
    所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
    ? 抢占式调度
    优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

    大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是在同时运行,”感觉这些软件好像在同一时刻运行着“。


    实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
    其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

    原因是:jvm启动后,必然有一个执行路径(线程)从main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程。当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行。
    那么,能否实现一个主线程负责执行其中一个循环,再由另一个线程负责其他代码的执行,最终实现多部分代码同时执行的效果?
    能够实现同时执行,通过Java中的多线程技术来解决该问题。


    ? 一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。
    ? 另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。


    SubThread类
    package cn.itcast.thread;

    public class SubThread extends Thread {
    //继承Thread,重写方法run()

    @Override
    public void run() {
    // TODO Auto-generated method stub
    for (int i = 0; i < 50; i++) {
    System.out.println("run" + i);
    }
    }

    }
    demo测试类
    package cn.itcast.thread;
    //如何创建和启动线程
    public class Demo {
    public static void main(String[] args) {
    SubThread st = new SubThread();
    st.start();//start()方法是父类的,必须调start(),jvm开启新线程才调用了run,直接手动手动调用run()是不对的,这样的话还是个单线程程序
    for (int i = 0; i < 50; i++) {
    System.out.println("main" + i);
    }

    }

    }
    结果:(每次运行都会变化)

    main0
    main1
    main2
    main3
    run0
    main4
    main5
    main6
    main7
    main8
    main9
    main10
    main11
    main12
    main13
    main14
    run1
    run2
    run3
    run4
    run5
    run6
    run7
    run8
    run9
    run10
    run11
    run12
    run13
    run14
    run15
    run16
    run17
    run18
    run19
    run20
    run21
    run22
    run23
    run24
    run25
    run26
    run27
    run28
    run29
    run30
    run31
    run32
    run33
    run34
    run35
    run36
    run37
    run38
    run39
    run40
    run41
    run42
    run43
    run44
    run45
    run46
    run47
    run48
    run49
    main15
    main16
    main17
    main18
    main19
    main20
    main21
    main22
    main23
    main24
    main25
    main26
    main27
    main28
    main29
    main30
    main31
    main32
    main33
    main34
    main35
    main36
    main37
    main38
    main39
    main40
    main41
    main42
    main43
    main44
    main45
    main46
    main47
    main48
    main49


    思考:线程对象调用 run方法和调用start方法区别?
    线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。


    我们为什么要继承Thread类,并调用其的start方法才能开启线程呢?
    继承Thread类:因为Thread类用来描述线程,具备线程应该有功能。那为什么不直接创建Thread类的对象呢?如下代码:
    Thread t1 = new Thread();
    t1.start();//这样做没有错,但是该start调用的是Thread类中的run方法,而这个run方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让线程执行的代码。

    创建线程的目的是什么?
    是为了建立程序单独的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定线程要执行的任务。
    对于之前所讲的主线程,它的任务定义在main函数中。自定义线程需要执行的任务都定义在run方法中。


    Thread类run方法中的任务并不是我们所需要的,只有重写这个run方法。既然Thread类已经定义了线程任务的编写位置(run方法),那么只要在编写位置(run方法)中定义任务代码即可。所以进行了重写run方法动作。


    ? Thread.currentThread()获取当前线程对象
    ? Thread.currentThread().getName();获取当前线程对象的名称

    通过结果观察,原来主线程的名称:main;自定义的线程:Thread-0,线程多个时,数字顺延。如Thread-1......


    异常看名字:
    NameThread类
    package cn.itcast.threadname;

    public class NameThread extends Thread {
    public void run() {
    System.out.println(0/0);
    }

    }
    ThreadDemo类
    package cn.itcast.threadname;

    public class ThreadDemo {
    public static void main(String[] args) {

    NameThread nt = new NameThread();
    //nt.run();
    /*
    *
    * Exception in thread "main" java.lang.ArithmeticException: / by zero
    at cn.itcast.threadname.NameThread.run(NameThread.java:5)
    at cn.itcast.threadname.ThreadDemo.main(ThreadDemo.java:7)

    */
    nt.start();

    /*
    *
    * Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
    at cn.itcast.threadname.NameThread.run(NameThread.java:5)

    */
    }
    }

    看线程名字:getName();
    package cn.itcast.threadname;

    public class NameThread extends Thread {
    public void run() {
    //System.out.println(0/0);
    System.out.println(getName());
    }
    }
    通用得到线程名字
    System.out.println(Thread.currentThread().getName());


    改线程名,先改名,后开启线程
    nt.setName("hah");

    子类构造器改名

    public NameThread(String name) {
    super(name);
    // TODO Auto-generated constructor stub
    }

    NameThread nt = new NameThread("hah");





    sleep是静态方法
    package cn.itcast.sleep;

    public class Demo {

    public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 5; i++) {
    Thread.sleep(50);//sleep是静态方法
    System.out.println(i);
    }
    }
    }

    多线程实现接口方式
    RunnableDemo类
    package cn.itcast.runnable;

    public class RunnableDemo {
    public static void main(String[] args) {

    SubRunnable sr = new SubRunnable();
    Thread t = new Thread(sr);
    t.start();
    for (int i = 0; i < 50; i++) {
    System.out.println("main--" + i);
    }

    }

    }


    SubRunnable类
    package cn.itcast.runnable;

    public class SubRunnable implements Runnable {

    @Override
    public void run() {
    // TODO Auto-generated method stub
    for (int i = 0; i < 50; i++) {
    System.out.println("run--" + i);
    }
    }

    }

    创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。

    实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。
    创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。


    1.6.2 实现Runnable的好处
    第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。

    匿名内部类实现多线程
    package cn.itcast.anonymous;

    public class Demo {
    public static void main(String[] args) {
    new Thread(){
    public void run() {
    System.out.println("---");
    }
    }.start();


    new Thread(new Runnable(){
    public void run(){
    System.out.println("0000000");
    }
    }).start();

    }
    }

    实现线程池
    package cn.itcast.threadpool;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    //使用jdk1.5新特性,实现线程池,使用工厂类Executor中的静态方法创建线程对象,指定线程的个数
    public class ThreadPoolDemo {

    public static void main(String[] args) {
    //调用工厂类创建线程池对象
    ExecutorService threadPool = Executors.newFixedThreadPool(2);
    threadPool.submit(new ThreadPoolRunnable());
    threadPool.submit(new ThreadPoolRunnable());
    threadPool.submit(new ThreadPoolRunnable());
    threadPool.shutdown();


    }

    }

    package cn.itcast.threadpool;

    public class ThreadPoolRunnable implements Runnable {

    @Override
    public void run() {
    // TODO Auto-generated method stub
    System.out.println("线程提交的任务" + Thread.currentThread().getName());
    }

    }

    实现Callable接口的多线程,可以有返回值,可以抛异常


    package cn.itcast.callable;

    import java.util.concurrent.Callable;

    public class CallableDemo implements Callable<String>{


    @Override
    public String call() throws Exception {
    // TODO Auto-generated method stub
    return "abc";
    }
    }


    package cn.itcast.callable;

    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;

    public class Demo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService es = Executors.newFixedThreadPool(2);
    Future<String> submit = es.submit(new CallableDemo());
    System.out.println(submit.get());
    }

    }


    案例:用多线程计算求和
    package cn.itcast.casedemo;

    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;

    public class CaseDemo {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService pool = Executors.newFixedThreadPool(2);
    Future<Integer> f1 = pool.submit(new SumCallable(100));
    Future<Integer> f2 = pool.submit(new SumCallable(200));
    System.out.println(f1.get() + "--------" + f2.get());
    pool.shutdown();
    }
    }

    package cn.itcast.casedemo;

    import java.util.concurrent.Callable;

    public class SumCallable implements Callable<Integer>{

    private int a;
    public SumCallable(int a) {
    this.a = a;
    }

    @Override
    public Integer call() throws Exception {
    int sum = 0;
    for (int i = 1; i <= a; i++) {
    sum += i;
    }
    return sum;
    }

    }


    注意call是没有参数的,传参只能通过构造函数.



  • 相关阅读:
    MangoDB相关文档阅读小结
    《算法导论》图相关算法小结
    关于GC(下):CMS和G1GC的比较
    《深入理解Java虚拟机》并发(第12~13章)笔记
    关于GC(中):Java垃圾回收相关基础知识
    关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程
    远程调用代码封装杂谈
    深入理解Java的switch...case...语句
    [留档]阿里云主机使用笔记
    企业架构设计之IFW实践回顾
  • 原文地址:https://www.cnblogs.com/zjj1996/p/9108036.html
Copyright © 2011-2022 走看看