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

    在Java中,如果要实现多线程,必须依靠一个线程的主体类(就好比主类的概念一样,表示的是一个线程的主类)。

    但是这个线程的主体类在定义时也需要一些特殊的要求,即类需要继承Thread类或实现Runnable(Callable)接口来完成定义

    多线程的实现

    继承Thread类,实现多线程

    public class MyThread extends Thread {    //多线程的操作类
        private String name ;
        public MyThread(String name) {
            this.name = name ;
        }
        @Override
        public void run() {    //覆写run方法作为线程的主操作类
                for(int x = 0 ; x <200 ;x++) {
                    System.out.println(this.name+"-->"+x);
                }
        }
    
    }
    
    
    main:
         MyThread mt1 = new MyThread("线程A");
         MyThread mt2 = new MyThread("线程B");
         MyThread mt3 = new MyThread("线程C");
         //启动多线程
         mt1.start();
         mt2.start();
         mt3.start();


    为什么多线程启动不是调用run()而必须调用start()?
      在java的开发里面有一门技术称为Java本地接口(Java Native Interface,JNI)技术。使用Java调用本地操作系统提供的函数。
    这个技术不能离开特定的操作系统,如果要执行线程,需要根据操作系统来进行资源的分配。主要是由JVM根据不同的操作系统来实现。
    即使用Thread类的start()方法不仅要启动多线程的执行代码,还要根据不同的操作系统进行资源分配

    实现Runnable接口,实现多线程

    public class MyThread2 implements Runnable {  //实现接口
        private String name ;
        public MyThread2(String name) {
            this.name = name ;
        }
        @Override
        public void run() {    //覆写run()
            for(int x = 0 ; x <200 ;x++) {
                System.out.println(this.name+"-->"+x);
            }
        }
    }


    main:

    /***实现Runnable接口的多线程,,Thread是Runnable接口 的子类(代理),
    * 通过Thread类对象包装Runnable接口对象实例,然后利用Thread 类的start()方法启动多线程***/
    MyThread2 mt01 = new MyThread2("线程1");
    MyThread2 mt02 = new MyThread2("线程2");
    MyThread2 mt03 = new MyThread2("线程3");

    new Thread(mt01).start();
    new Thread(mt02).start();
    new Thread(mt03).start();

    使用Runnable接口可以有效避免单继承局限问题,所以在实际的开发中对于多线程的实现首选Runnable接口

    两种实现方式的区别

    public class Thread extends Object implements Runnable

    通过定义可以发现,Thread类也是Runnable接口的子类,之前利用Runnable接口实现的多线程,实际结构:

    Runnable接口                     Thread类

    class MyThread  implements Runnable{          class MyThread extends Thread(){

    @Override                         @Override       

      public void run(){//线程主方法                 public void run(){  //线程主方法     

      //线程操作方法                        //线程操作方法

      }                              }          

    }                             }

    MyThread mt = new MyThread();              MyThread mt = new MyThread();

    new Thread(mt).start();                    mt.start();

    利用Callable接口实现多线程

    使用Runnable接口可以避免单继承的局限性,但是Runnable接口里面的run()方法不能返回操作结果。

    从jdk1.5开始提供了新的接口:  java.util.concurrent.Callable

    @FunctionalInterface
    public interface Callable<V>{
          public V call() throws Exception;          
    }
    public class MyThread implements Callable<String> {
    
        private int ticket = 0 ;
        @Override
        public String call() throws Exception {
            for(int i = 0 ; i <100; i ++) {
                if(this.ticket > 0) {
                    System.out.println("卖出,剩余"+this.ticket --);
                }
            }
            return "票卖完了!";   //返回结果
        }
    }

    如何启动实现Callable接口的多线程?
    Thread类没有定义构造方法可以直接接收Callble接口对象实例,并且由于需要接收call()方法返回值的问题。JDK1.5开始,提供了java.util.concurrent.FutureTask<V>
    public class FutureTask<V> extends Object implements RunnableFuture<V>

    /***实现了callable 接口的多线程,可以返回结果;
    * RunnableFuture接口 实现了Runnable接口和Future接口
    * FutureTask 又实现了 RunnableFuture 接口
    * ***/
    MyThread mt1 = new MyThread();
    MyThread mt2 = new MyThread();

    FutureTask<String> task1 = new FutureTask<String>(mt1);
    FutureTask<String> task2 = new FutureTask<String>(mt2);
    //FutureTask是Runnable接口子类,所以可以使用Thread类的构造来接收task对象

    new Thread(task1).start();
    new Thread(task2).start();

    //多线程执行完毕后可以取得内容
    System.out.println("A线程的返回结果"+task1.get());
    System.out.println("B线程的返回结果"+task2.get());

    线程的操作状态

      1.创建状态

        在程序中用构造方法创建一个线程对象后,新的线程对象便处于新建状态。此时已经有相应内存空间和其他资源,但处于不可运行状态。

      2.就绪状态

        新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时线程将进入线程队列排队,等待cpu服务,这表明它已经具备了运行状态。

      3.运行状态

        当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。

      4.堵塞状态

        一个正在执行的线程在某些情况下,如被人为挂起或需要执行耗时的输入输出操作时,将让出CPU,并暂时中止自己的执行,进入堵塞状态。在可执行状态下,如果调用sleep()、suspend()、wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引起堵塞的原因被消除后,线程才可以转入就绪状态。

      5.终止状态

      线程调用stop()方法时候run()方法执行结束后,就处于终止状态。处于终止状态的线程不具有继续运行的能力

    多线程常用操作方法

    线程的命名与取得

    public Thread(Runnable target, String name)  构造方法  实例化线程对象,接受Runnable接口子类对象,同时设置线程名字

    public final void setName(String name)  普通方法  设置线程名字

    public final String getName()  普通方法  取得线程名字

    进程在哪里?

      当用户使用Java命令执行一个类时就表示启动了一个JVM的进程,而主方法只是进程上的一个线程而已,当一个类执行完毕后,此进程会自动消失。

      而且每个JVM进程都至少启动一下两个线程:

      • main线程:程序的主要执行,以及启动子线程
      • gc线程:负责垃圾的收集。          

    线程的休眠

     public static void sleep(long millis) throws InterruptedException ,设置的休眠单位时间是毫秒(ms)

    线程优先级

      在Java线程操作中,所有的线程在运行前都会保持就绪状态,此时哪个线程的优先级高,就可能先被执行。

    public static final int MAX_PRIORITY  常量  最高优先级,数值为10

    public static final int NORM_PRIORITY  常量  中等优先级,数值为5

    public staic final int MIN_PRIORITY  常量  最低优先级,数值为1

    public final void setPriority(int newPriority)  普通  设置线程优先级

    public final int getPriority()  普通  取得线程优先级

    线程的同步与死锁

    当多个线程操作同一资源时,就有可能出现不同步的情况:还没有等到前一个线程的执行结果就进行了下一个线程的操作,从而出现问题。

    例如:

      多个线程进行卖票操作,当票数为1的时候,前一个线程获取到数量为1的票数,还没等它的执行完成,后一个线程也获取到了数量为1的票数开始执行。最终出现问题

    同步操作

    一个代码块中的多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成后才可以继续执行。

    实现同步操作可以使用synchronized关键字。synchronized关键字可以通过以下两种方式进行使用:

      1.同步代码块,利用synchronized包装的代码块,但是需要指定同步对象,一般设置为this;

      2.同步方法,利用synchronized定义的方法

    同步代码块:
    
    public MyThread implements Runnable{
     
      private int ticket = 5 ;
      @Override
      public void run(){
         for(int x =0;x<20;x++){
          synchronized(this){
          if(this.ticket > 0){
            //卖票操作
          }
         }     } } }

    同步方法:
    public MyThread implements Runnable{
     
      private int ticket = 5 ;
      @Override
      public void run(){
         for(int x =0;x<20;x++){
          this.sale()  //调用synchronized方法
       } }

    public synchronized void sale(){
        //卖票操作。。。
    }

    }
     

    abstract的method是否可以同时是static,是否可以同时是native,是否可以同时是synchronized?

      method,static,natice,synchronized都不能和“abstract”同时声明方法

    当一个线程进入一个对象的synchronized方法后,其他线程是否可以访问此对象的其他方法?

      不能访问,一个对象操作一个synchronized方法只能由一个线程访问。

    死锁

    概念:两个线程都在等待彼此先完成,造成了程序的停滞状态,一般程序的死锁都是在程序运行时出现的。过多的同步也会造成死锁。

    生产者和消费者问题

    线程的生命周期

  • 相关阅读:
    课堂作业04 2017.10.27
    课程作业 03 动手动脑 2017.10.20
    课程作业 03 2017.10.20
    HDU 3974 Assign the task
    POJ 2155 Matrix
    POJ 2481 Cows
    HDU 3038 How Many Answers Are Wrong
    CS Academy Array Removal
    POJ_1330 Nearest Common Ancestors LCA
    CF Round 427 D. Palindromic characteristics
  • 原文地址:https://www.cnblogs.com/Thomas-blog/p/9735867.html
Copyright © 2011-2022 走看看