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();

ide

使用Runnable接口能够有效避免单继承局限问题,因此在实际的开发中对于多线程的实现首选Runnable接口函数

两种实现方式的区别

public class Thread extends Object implements Runnablethis

经过定义能够发现,Thread类也是Runnable接口的子类,以前利用Runnable接口实现的多线程,实际结构:spa

Runnable接口                     Thread类操作系统

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

@Override                         @Override       3d

  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方法只能由一个线程访问。

死锁

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

生产者和消费者问题

线程的生命周期

相关文章
相关标签/搜索