Java多线程

Java是一门为数不多的多线程支持的编程语言。
如果要想解释多线程之前,首先需要知道什么是进程?在操作系统中,进程指的是一次程序的完整执行,在这个运行的过程之中内存、处理器、IO等资源操作都要为这个进程进行服务。线程是在进程上进一步划分的结果,即:一个进程可以同时创建多个线程。线程是比进程更快的处理单元,而且所占的资源也小。那么多线程的应用也就是性能最高的应用。

1. 多线程的实现

   如果想在Java之中实现多线程有两种途径:
    · 继承Thread 类
    · 实现Runnable接口(Callable接口)

1.1  继承Thread类
   Thread类是一个支持多线程的功能类,只要有一个子类它就可以实现多线程的支持。
class MyThread extends Thread{  //这就是一个多线程的操作类
}
     所有程序的起点是main()方法,但是所有的线程也一定要有自己的一个起点,那么这个起点就是run()方法,在多线程的每个主体类之中都必须覆写Thread类中所提供的run()方法 public void run() {}  
     这个方法上没有返回值,那么也就表示了线程一旦开始就要一直执行,不能够返回内容。
范例:多线程启动
package cn.mldn.demo;
class MyThread extends Thread { //这就是一个多线程的操作类
private String name;
    public MyThread(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
@Override
public void run() { //覆写run方法,作为线程的主体操作方法
for (int x = 0; x < 200; x ++) {
System.out.println(this.name+"--->"+ x);
}
}
}
public class TestDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread mt1 = new MyThread("线程1");
MyThread mt2 = new MyThread("线程2");
MyThread mt3 = new MyThread("线程3");

mt1.start();
mt2.start();
mt3.start();
}
}
此时每一个线程对象交替执行。
疑问?为什么多线程启动不是调用run()而必须调用start().
   start()方法里面要调用一个start0()方法,而且此方法的结构与抽象方法类似,使用了native声明,在Java开发里面有一门技术JNI(Java Native Interface),这门技术的特点,是使用Java调用本机操作系统提供的函数。但是这样的技术有一个缺点,不能够离开特定的操作系统。 如果要想线程能够执行,需要操作系统来进行资源分配,所以此操作严格来讲主要是由JVM负责根据不同的操作系统而实现的。
   即:使用Thread类的start()方法,不仅仅是要启动多线程的执行代码,还要去根据不同的操作系统进行资源的分配。

1.2 实现Runnable接口
   虽然Thread类可以实现多线程的主体类定义,但是它有一个问题,Java具有单继承局限,正因为如此,在任何情况下,针对于类的继承都应该是回避的问题,那么多线程也一样,为了解决单继承的限制,在Java里面专门提供了Runnable接口。此接口定义如下:
@FunctionalInterface
public interface Runnable{
   public void run();
}

   在接口里面任何方法都是public定义的权限,不存在默认的权限。
   那么只需要让一个类实现Runnable接口即可,并且也需要覆写run()方法。
   与继承Thread类相比,此时的MyThread类在结构上与之前是没有区别的,但是继承了Thread类
,那么可以直接继承start()方法,但是如果实现Runnable接口,并没有start()方法可以继承。 不管何种情况下,如果想要启动多线程一定要考Thread类完成,在Thread类里面定义有以下的定义方法。
    ·构造方法:public Thread(Runnable target),接收的是Runnable接口的子类
范例:启动多线程
    package cn.mldn.demo;

class MyThread implements Runnable { //这就是一个多线程的操作类
private String name;
    public MyThread(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
@Override
public void run() { //覆写run方法,作为线程的主体操作方法
for (int x = 0; x < 200; x ++) {
System.out.println(this.name+"--->"+ x);
}
}
}
public class TestDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
MyThread mt3 = new MyThread("线程C");

new Thread(mt1).start();
new Thread(mt2).start();
new Thread(mt3).start();
}
}
此时就避免了单继承局限,那么也就是说在实际工作中继承Runnable接口是最合适的。

2. 多线程两种方式的区别

   首先一定要明确的是,使用Runnable接口与Thread类相比,解决了单继承的定义局限,所以不管后面的区别与联系是什么,至少这一点上就已经下了死定义--如果要使用多线程一定要继承Runnable 接口。
   首先观察一下Thread类的定义,
public class Thread extends Object implements Runnable
   发现Thread类实现了Runnable接口。此时整个的定义结构看起来非常像代理设计模式,如果是代理设计模式,客户端调用的代理类的方法也应该是接口里的run()才对。

   除了以上的联系之外,还有一点:使用Runnable接口可以比Thread类能够更好的描述出数据共享的概念。此时的数据共享指的是多个线程访问同一资源的操作。


3. 第三种实现方式(理解)

   使用Runnable接口实现的多线程可以避免单继承局限,的确很好,但是有一个问题,Runnable接口里面的run()方法,并不能返回操作结果。为了解决这样的矛盾,提供了一个新的接口 java.util.concurrent.Callable接口。

   @FunctionalInterface
public interface Callable<V>{
    public V call() throws Exception;
}

   call()方法执行完线程的主题功能之后可以返回一个结果,而返回结果的类型由Callable接口的泛型决定。

范例:多线程实现

package cn.mldn.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String> { //这就是一个多线程的操作类
   private int ticket = 10;
@Override
    public String call() throws Exception {
    // TODO Auto-generated method stub
    for(int x= 0; x< 100; x++) {
    if(this.ticket > 0) {
    System.out.println("卖票,ticket = "+ this.ticket --);
    }
    }
    return "票已卖光!";
    }
}
public class TestDemo {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
               FutureTask<String> t1 = new FutureTask<String>(mt1);//目的是为了取得call()方法的返回结果
               FutureTask<String> t2 = new FutureTask<String>(mt2);

               //FutureTask是Runnable接口的子类,所以能够使用Thread类的构造来接收task对象
new Thread(t1).start();//启动多线程

new Thread(t2).start();

               //多线程执行完毕后可以取得内容,依靠FutureTask的父接口Future中的get()方法完成
System.out.println("task1的返回结果:"+t1.get());
System.out.println("task2的返回结果:"+t2.get());
}
}
  第三种方式,最麻烦的问题在于需要接收返回值信息,又要与原始的多线程的实现靠拢。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值