【线程定义】
   定义:线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

【实现方法】
   1. 第一种继承:Thread类
class 类名extends Thread{
   方法1;
   方法2;
   ……
   public void run(){
     实现代码
    }
}
   2. 第二种:实现Runnable接口
class 类名 implements Runnable{
   方法1;
   方法2;
   ……
   public void run(){
     实现代码
   }
}

【三段代码】
      第一段代码,就是一个很简单,就是和多线程没有关系的一段测试代码。
      第二段代码,使用继承Thread的方法实现多线程。
      第三段代码,使用实现Runnable接口的方法实现多线程。

第一段代码:
public class NoThreadTest {
    private String noThread1;
    public NoThreadTest(){
    }
    public NoThreadTest(String noThread1){
        this.noThread1=noThread1;
    }
    public void run(){
        for(int i=0;i<3;i++){
            System.out.println(noThread1+"非多线程运行结果                             "+i);
        }
    }
    public static void main(String[] args){
        NoThreadTest maintest1=new NoThreadTest("我是A");
        NoThreadTest maintest2=new NoThreadTest("我是B");
        maintest1.run();
        maintest2.run();
    }

执行结果:
我是A非多线程运行结果                                 0
我是A非多线程运行结果                                 1
我是A非多线程运行结果                                 2
我是B非多线程运行结果                                 0
我是B非多线程运行结果                                 1
我是B非多线程运行结果                                 2

第二段代码:
public class ThreadTest extends Thread {
    private String Thread1;
    public ThreadTest(){
    }
    public ThreadTest(String Thread1){
        this.Thread1=Thread1;
    }
    public void run(){
        for(int i=0;i<3;i++){//可以参看评论5进行修改,效果会好一些。
            System.out.println(Thread1+"多线程运行结果                                 "+i);
        }
    }
    public static void main(String[] args){
        ThreadTest maintest1=new ThreadTest("我是A");
        ThreadTest maintest2=new ThreadTest("我是B");
        maintest1.run();
        maintest2.run();
        System.out.println("..............我是分割线........................");
        maintest1.start();
        maintest2.start();
    }
}

执行结果:(每一次执行的结果都是不一样的,这只是其中的某一种)
我是A多线程运行结果                                 0
我是A多线程运行结果                                 1
我是A多线程运行结果                                 2
我是B多线程运行结果                                 0
我是B多线程运行结果                                 1
我是B多线程运行结果                                 2
..............我是分割线........................
我是A多线程运行结果                                 0
我是B多线程运行结果                                 0
我是B多线程运行结果                                 1
我是B多线程运行结果                                 2
我是A多线程运行结果                                 1
我是A多线程运行结果                                 2

第三段代码:
public class ThreadTest2 implements Runnable {
    private String Thread1;
    public ThreadTest2(){
    }
    public ThreadTest2(String Thread1){
        this.Thread1=Thread1;
    }
    public void run(){
        for(int i=0;i<3;i++){//可以参看评论5,效果会好一些
            System.out.println(Thread1+"多线程运行结果                                 "+i);
        }
    }
    public static void main(String[] args){
        ThreadTest2 maintest1=new ThreadTest2("我是A");
        ThreadTest2 maintest2=new ThreadTest2("我是B");
        Thread maintest4=new Thread(maintest1);
        Thread maintest5=new Thread(maintest2);
        maintest1.run();
        maintest2.run();
        System.out.println("..............我是分割线1........................");
        maintest4.run();
        maintest5.run();
        System.out.println("..............我是分割线2........................");
        maintest4.start();
        maintest5.start();
    }
}

执行结果:(每一次执行的结果都是不一样的,这只是其中的某一种)
我是A多线程运行结果                                 0
我是A多线程运行结果                                 1
我是A多线程运行结果                                 2
我是B多线程运行结果                                 0
我是B多线程运行结果                                 1
我是B多线程运行结果                                 2
..............我是分割线1........................
我是A多线程运行结果                                 0
我是A多线程运行结果                                 1
我是A多线程运行结果                                 2
我是B多线程运行结果                                 0
我是B多线程运行结果                                 1
我是B多线程运行结果                                 2
..............我是分割线2........................
我是B多线程运行结果                                 0
我是B多线程运行结果                                 1
我是B多线程运行结果                                 2
我是A多线程运行结果                                 0
我是A多线程运行结果                                 1
我是A多线程运行结果                                 2

【代码分析】
    1. run方法和start方法的区别:
       java核心技术中有这样一段话:不要调用Thread类或Runnable对象的run方法。直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用Thread.start方法。这个方法将创建一个执行run方法的新线程。
   2. 在上面第三段代码中,ThreadTest2类,Thread类和Runnerable接口都实现了run方法。
     1)Runnerable接口实现run方法
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

2)Thread类实现run方法。
public void run() {
    if (target != null) {
        target.run();
    }
}

3)为什么要说这个?因为这个实现是使用了代理模式。
     代理:一个角色代表另一个角色来完成某些特定的功能。
     举个例子:大家都买过衣服,所以在买衣服的时候,一般有下面的角色:
     购物者:我们一般是从代理商那里买衣服,我们并不和制造商进行交涉,我们不关心衣服是如何生产出来的。
     代理商:代理商是从制造商那里拿衣服,并且代理商可以提供一些简单的服务,比如裁剪裤子等。
     制造商:制造衣服,并且批发衣服给代理商。
     我们从上面的行为中可以抽象出,一个行为就是卖衣服这个行为在代理商和制造商都有,如果购物者要买衣服,也需要以代理商和制造商卖衣服为前提。
     从上面我们可以抽象出三个角色,并不是和上面对应的哈。
     抽象主题角色:这个使我们可以抽象出来的角色。就是卖衣服这个行为。
     代理主题角色:中间商。
     实际被代理角色:制造商。
    代码:
    SellClothes.java(需要和接口名一样)
//抽象主题角色:买衣服
public interface SellClothes {
  void sellClothes();
}

Middleman.java
//代理主题角色:中间商
public class Middleman implements SellClothes{
  private SellClothes t;
  public Middleman(SellClothes t) {
   super();
   this.t = t;
  }
  public void sellClothes() {
      t.sellClothes();
      System.out.println("我是中间商,我买的是制造商的衣服");
      System.out.println("我是中间商,我还提供对裤子不合适的进行裁剪服务");
}
}

SellClothes.java
//实际被代理角色
public class Manufacturer implements SellClothes{
  public void sellClothes() {
   System.out.println("我是制造商,我提供批发衣服服务");
  }
}

Test.java
public class Test {
  public static void main(String[] args) {
   Manufacturer t = new Manufacturer();
   Middleman sellclothes = new Middleman(t);
   sellclothes.sellClothes();
  }
}

运行结果:
我是制造商,我提供批发衣服服务
我是中间商,我买的是制造商的衣服
我是中间商,我还提供对裤子不合适的进行裁剪服务

得出结论:
抽象主题角色:Runnable,提供run方法
代理主题角色:Thread类,提供run方法
实际被代理角色:ThreadTest2,也实现了run 方法

【实现区别】
    1. 三段代码
     1)第一段:
public class ThreadTest6 extends Thread {
    private int num=3;//定义飞机票的张数
    public void run(){
        for(int i=0;i<10;i++){
            if(num>0){
                System.out.println(Thread.currentThread().getName()+"飞机票还剩余num= "+num--);
            }
        }        
    }
    public static void main(String[] args){
        ThreadTest6 threadtest1=new ThreadTest6();
        ThreadTest6 threadtest2=new ThreadTest6();
        ThreadTest6 threadtest3=new ThreadTest6();
        threadtest1.start();
        threadtest2.start();
        threadtest3.start();

}
}

结果:
Thread-1飞机票还剩余num= 3
Thread-2飞机票还剩余num= 3
Thread-2飞机票还剩余num= 2
Thread-2飞机票还剩余num= 1
Thread-0飞机票还剩余num= 3
Thread-0飞机票还剩余num= 2
Thread-0飞机票还剩余num= 1
Thread-1飞机票还剩余num= 2
Thread-1飞机票还剩余num= 1

2)第二段:
public class ThreadTest5 implements Runnable {
    private int num=3;//定义飞机票的张数
    public void run(){
        for(int i=0;i<10;i++){
            if(num>0){
                System.out.println(Thread.currentThread().getName()+"飞机票还剩余num= "+num--);
            }
        }        
    }
    public static void main(String[] args){
        ThreadTest5 threadtest1=new ThreadTest5();
        ThreadTest5 threadtest2=new ThreadTest5();
        ThreadTest5 threadtest3=new ThreadTest5();
        Thread thread1=new Thread(threadtest1,"窗口1");
        Thread thread2=new Thread(threadtest2,"窗口2");
        Thread thread3=new Thread(threadtest3,"窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

结果:
窗口2飞机票还剩余num= 3
窗口2飞机票还剩余num= 2
窗口2飞机票还剩余num= 1
窗口1飞机票还剩余num= 3
窗口1飞机票还剩余num= 2
窗口1飞机票还剩余num= 1
窗口3飞机票还剩余num= 3
窗口3飞机票还剩余num= 2
窗口3飞机票还剩余num= 1

3)第三段:
public class ThreadTest7 implements Runnable {
    private int num=3;//定义飞机票的张数
    public void run(){
        for(int i=0;i<10;i++){
            if(num>0){
                System.out.println(Thread.currentThread().getName()+"飞机票还剩余num= "+num--);
            }
        }        
    }
    public static void main(String[] args){
        ThreadTest7 threadtest1=new ThreadTest7();
        Thread thread1=new Thread(threadtest1,"窗口1");
        Thread thread2=new Thread(threadtest1,"窗口2");
        Thread thread3=new Thread(threadtest1,"窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

结果:
窗口1飞机票还剩余num= 3
窗口1飞机票还剩余num= 2
窗口1飞机票还剩余num= 1

2. 分析:
      第一和第二段代码不管是使用Runnerable实现还是使用Thread实现,从结果可以看出三个线程每个线程均有num这个资源,如果把num看做是飞机票的话,那么每个线程都有三张飞机票。这不是我们实际中要得到的情况。
      要实现这个飞机售票程序,我们只能创建一个资源对象,但要创建多个线程去处理同一个资源对象,并且每个线程上所运行的是相同的程序代码。
      这就是第三段代码实现的结果,ThreadTest7只创建了一个资源对象——threadtest1。创建了三个线程,每个线程调用的是同一个threadtest1对象中的run()方法,访问的是同一个对象中的变量(num)的实例,这个程序满足了我们的需求。

3. 结论:
      可见,实现Runnable接口相对于继承Thread类来说,有如下显著的好处:
      (1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。
      (2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。
      (3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。

java多线程的认识的更多相关文章

  1. 40个Java多线程问题总结

    前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...

  2. Java多线程基础知识篇

    这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...

  3. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  4. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

  5. Java多线程--让主线程等待子线程执行完毕

    使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待. java.util.concurrent.CountDownLatch 使用c ...

  6. Java多线程 2 线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

  7. java 多线程 1 线程 进程

    Java多线程(一).多线程的基本概念和使用 2012-09-10 16:06 5108人阅读 评论(0) 收藏 举报  分类: javaSE综合知识点(14)  版权声明:本文为博主原创文章,未经博 ...

  8. 一起阅读《Java多线程编程核心技术》

    目录 第一章 Java多线程技能 (待续...)

  9. 第一章 Java多线程技能

    1.初步了解"进程"."线程"."多线程" 说到多线程,大多都会联系到"进程"和"线程".那么这两者 ...

  10. java从基础知识(十)java多线程(下)

    首先介绍可见性.原子性.有序性.重排序这几个概念 原子性:即一个操作或多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行. 可见性:一个线程对共享变量值的修改,能够及时地被其它线程看到 ...

随机推荐

  1. 维护贴--linux下 mysql数据库的备份和还原 (转)

    1.备份 1 [root@CentOS ~]# mysqldump -u root -p mysql > ~/mysql.sql #把数据库mysql备份到家目录下命名为mysql.sql 2 ...

  2. VS未能正确加载包

    这个方法同样适用于多个版本的VS 首先关闭VS进这里:C:\Users\用户名\AppData\Roaming\Microsoft\VisualStudio\11.0(2012是11.0,根据你的VS ...

  3. spring邮件发送

    1,Emaill类: package com.learn.jsp.pojo; /** * 邮件基本信息 * @author kevin * */public class Email { private ...

  4. python cntl使用

    import sys 2 import time 3 import fcntl 4 5 class FLOCK(object): 6 7 def __init__(self, name): 8 sel ...

  5. RichEdit选中文字右键菜单的实现

    procedure TForm1.RichEdit1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: ...

  6. Flsk-Werkzeug-请求参数获取备忘

    Werkzeug:response,request,routing 获取请求参数:data,form,args,files,cookies,headers,method,url routing:Rul ...

  7. 【mysql】之性能优化

    http://blog.csdn.net/orichisonic/article/details/48026031 https://www.cnblogs.com/zhming26/p/6322353 ...

  8. centos7 安装 codeblock(rpm)

    --------------------- 1.yum -y install epel-release 2.yum clean all && yum makecache 3.yum - ...

  9. bzoj5050: 建造摩天楼

    Description 属于小Q管辖的n座摩天楼从左往右排成一排,编号依次为1到n,第i座摩天楼的高度为h_i.小Q会进行m次以下两种 操作之一: 2 l r,询问h_l+h_{l+1}+...+h_ ...

  10. hadoop长时间运行后,stop-all.sh报错

    报错现象: hadoop在stop-all.sh的时候依据的是datanode上的mapred和dfs进程号. 而默认的进程号保存在/tmp下,linux默认会每 隔一段时间(一般是一个月或者7天左右 ...