Java学习笔记(八)——java多线程

【前面的话】

      实际项目在用spring框架结合dubbo框架做一个系统,虽然也负责了一块内容,但是自己的能力还是不足,所以还需要好好学习一下基础知识,然后做一些笔记。希望做完了这个项目可以写一些dubbo框架和spring框架方面的总结。

      学习过程中的小知识点总结,基础知识,选择阅读

【线程定义】

      在学习操作系统的时候,学习过什么是进程,什么是线程,下面这只维基百科里面关于线程的定义,大家可以看一下:

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

【实现方法】

   1. 第一种继承:Thread类

1 class 类名extends Thread{
2   方法1;
3   方法2;
4   ……
5   public void run(){
6     实现代码
7    }
8 }

   2. 第二种:实现Runnable接口

1 class 类名 implements Runnable{
2    方法1;
3    方法2;
4    ……
5    public void run(){
6      实现代码
7    }
8 }

【三段代码】

      第一段代码,就是一个很简单,就是和多线程没有关系的一段测试代码。

      第二段代码,使用继承Thread的方法实现多线程。

      第三段代码,使用实现Runnable接口的方法实现多线程。

第一段代码:

 1 public class NoThreadTest {
 2     private String noThread1;
 3     public NoThreadTest(){
 4     }
 5     public NoThreadTest(String noThread1){
 6         this.noThread1=noThread1;
 7     }
 8     public void run(){
 9         for(int i=0;i<3;i++){
10             System.out.println(noThread1+"非多线程运行结果                             "+i);
11         }
12     }
13     public static void main(String[] args){
14         NoThreadTest maintest1=new NoThreadTest("我是A");
15         NoThreadTest maintest2=new NoThreadTest("我是B");
16         maintest1.run();
17         maintest2.run();
18     }
View Code

执行结果:

1 我是A非多线程运行结果                                 0
2 我是A非多线程运行结果                                 1
3 我是A非多线程运行结果                                 2
4 我是B非多线程运行结果                                 0
5 我是B非多线程运行结果                                 1
6 我是B非多线程运行结果                                 2
View Code

第二段代码: 

 1 public class ThreadTest extends Thread {
 2     private String Thread1;
 3     public ThreadTest(){
 4     }
 5     public ThreadTest(String Thread1){
 6         this.Thread1=Thread1;
 7     }
 8     public void run(){
 9         for(int i=0;i<3;i++){//可以参看评论5进行修改,效果会好一些。
10             System.out.println(Thread1+"多线程运行结果                                 "+i);
11         }
12     }
13     public static void main(String[] args){
14         ThreadTest maintest1=new ThreadTest("我是A");
15         ThreadTest maintest2=new ThreadTest("我是B");
16         maintest1.run();
17         maintest2.run();
18         System.out.println("..............我是分割线........................");
19         maintest1.start();
20         maintest2.start();
21     }
22 }
View Code

执行结果:(每一次执行的结果都是不一样的,这只是其中的某一种)

 1 我是A多线程运行结果                                 0
 2 我是A多线程运行结果                                 1
 3 我是A多线程运行结果                                 2
 4 我是B多线程运行结果                                 0
 5 我是B多线程运行结果                                 1
 6 我是B多线程运行结果                                 2
 7 ..............我是分割线........................
 8 我是A多线程运行结果                                 0
 9 我是B多线程运行结果                                 0
10 我是B多线程运行结果                                 1
11 我是B多线程运行结果                                 2
12 我是A多线程运行结果                                 1
13 我是A多线程运行结果                                 2
View Code

第三段代码:

 1 public class ThreadTest2 implements Runnable {
 2     private String Thread1;
 3     public ThreadTest2(){
 4     }
 5     public ThreadTest2(String Thread1){
 6         this.Thread1=Thread1;
 7     }
 8     public void run(){
 9         for(int i=0;i<3;i++){//可以参看评论5,效果会好一些
10             System.out.println(Thread1+"多线程运行结果                                 "+i);
11         }
12     }
13     public static void main(String[] args){
14         ThreadTest2 maintest1=new ThreadTest2("我是A");
15         ThreadTest2 maintest2=new ThreadTest2("我是B");
16         Thread maintest4=new Thread(maintest1);
17         Thread maintest5=new Thread(maintest2);
18         maintest1.run();
19         maintest2.run();
20         System.out.println("..............我是分割线1........................");
21         maintest4.run();
22         maintest5.run();
23         System.out.println("..............我是分割线2........................");
24         maintest4.start();
25         maintest5.start();
26     }
27 }
View Code

执行结果:(每一次执行的结果都是不一样的,这只是其中的某一种)

 1 我是A多线程运行结果                                 0
 2 我是A多线程运行结果                                 1
 3 我是A多线程运行结果                                 2
 4 我是B多线程运行结果                                 0
 5 我是B多线程运行结果                                 1
 6 我是B多线程运行结果                                 2
 7 ..............我是分割线1........................
 8 我是A多线程运行结果                                 0
 9 我是A多线程运行结果                                 1
10 我是A多线程运行结果                                 2
11 我是B多线程运行结果                                 0
12 我是B多线程运行结果                                 1
13 我是B多线程运行结果                                 2
14 ..............我是分割线2........................
15 我是B多线程运行结果                                 0
16 我是B多线程运行结果                                 1
17 我是B多线程运行结果                                 2
18 我是A多线程运行结果                                 0
19 我是A多线程运行结果                                 1
20 我是A多线程运行结果                                 2
View Code

【代码分析】

    1. run方法和start方法的区别:

       java核心技术中有这样一段话:不要调用Thread类或Runnable对象的run方法。直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用Thread.start方法。这个方法将创建一个执行run方法的新线程。

   2. 在上面第三段代码中,ThreadTest2类,Thread类和Runnerable接口都实现了run方法。

     1)Runnerable接口实现run方法

 1 public
 2 interface Runnable {
 3     /**
 4      * When an object implementing interface <code>Runnable</code> is used 
 5      * to create a thread, starting the thread causes the object's 
 6      * <code>run</code> method to be called in that separately executing 
 7      * thread. 
 8      * <p>
 9      * The general contract of the method <code>run</code> is that it may 
10      * take any action whatsoever.
11      *
12      * @see     java.lang.Thread#run()
13      */
14     public abstract void run();
15 }
View Code

     2)Thread类实现run方法。

1 public void run() {
2     if (target != null) {
3         target.run();
4     }
5     }
View Code

    3)为什么要说这个?因为这个实现是使用了代理模式。

     代理:一个角色代表另一个角色来完成某些特定的功能。

     举个例子:大家都买过衣服,所以在买衣服的时候,一般有下面的角色:

     购物者:我们一般是从代理商那里买衣服,我们并不和制造商进行交涉,我们不关心衣服是如何生产出来的。

     代理商:代理商是从制造商那里拿衣服,并且代理商可以提供一些简单的服务,比如裁剪裤子等。

     制造商:制造衣服,并且批发衣服给代理商。

     我们从上面的行为中可以抽象出,一个行为就是卖衣服这个行为在代理商和制造商都有,如果购物者要买衣服,也需要以代理商和制造商卖衣服为前提。

     从上面我们可以抽象出三个角色,并不是和上面对应的哈。

     抽象主题角色这个使我们可以抽象出来的角色。就是卖衣服这个行为。

     代理主题角色:中间商。

     实际被代理角色:制造商。

    代码:

  • SellClothes.java(需要和接口名一样)
1 //抽象主题角色:买衣服
2 public interface SellClothes {
3   void sellClothes();
4 }
View Code
  • Middleman.java
 1 //代理主题角色:中间商
 2 public class Middleman implements SellClothes{
 3   private SellClothes t;
 4   public Middleman(SellClothes t) {
 5    super();
 6    this.t = t;
 7   }
 8   public void sellClothes() {
 9       t.sellClothes();
10       System.out.println("我是中间商,我买的是制造商的衣服");
11       System.out.println("我是中间商,我还提供对裤子不合适的进行裁剪服务");
12 }
13 }
View Code
  • SellClothes.java

1 //实际被代理角色
2 public class Manufacturer implements SellClothes{
3   public void sellClothes() {
4    System.out.println("我是制造商,我提供批发衣服服务");
5   }
6 }
View Code
  • Test.java
1 public class Test {
2   public static void main(String[] args) {
3    Manufacturer t = new Manufacturer();
4    Middleman sellclothes = new Middleman(t);
5    sellclothes.sellClothes();
6   }
7 } 
View Code

     运行结果:

1 我是制造商,我提供批发衣服服务
2 我是中间商,我买的是制造商的衣服
3 我是中间商,我还提供对裤子不合适的进行裁剪服务
View Code

    得出结论:

    抽象主题角色:Runnable,提供run方法

    代理主题角色:Thread类,提供run方法

    实际被代理角色:ThreadTest2,也实现了run 方法

    这就是代理主题模式,我希望我讲清楚了,哈哈

    3. native关键字

     在看到start()实现方法的时候,看到了如下一段代码:

 1   public synchronized void start() {
 2         /**
 3      * This method is not invoked for the main method thread or "system"
 4      * group threads created/set up by the VM. Any new functionality added 
 5      * to this method in the future may have to also be added to the VM.
 6      *
 7      * A zero status value corresponds to state "NEW".
 8          */
 9         if (threadStatus != 0)
10             throw new IllegalThreadStateException();
11         group.add(this);
12         start0();
13         if (stopBeforeStart) {
14         stop0(throwableFromStop);
15     }
16     }
17 private native void start0();
View Code

    然后我好奇,native是什么关键字:

    一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现

【实现区别】

    1. 三段代码

     1)第一段:

 1 public class ThreadTest6 extends Thread {
 2     private int num=3;//定义飞机票的张数
 3     public void run(){
 4         for(int i=0;i<10;i++){
 5             if(num>0){
 6                 System.out.println(Thread.currentThread().getName()+"飞机票还剩余num= "+num--);
 7             }
 8         }        
 9     }
10     public static void main(String[] args){
11         ThreadTest6 threadtest1=new ThreadTest6();
12         ThreadTest6 threadtest2=new ThreadTest6();
13         ThreadTest6 threadtest3=new ThreadTest6();
14         threadtest1.start();
15         threadtest2.start();
16         threadtest3.start();
17 
18     }
19 }
View Code

      结果:

1 Thread-1飞机票还剩余num= 3
2 Thread-2飞机票还剩余num= 3
3 Thread-2飞机票还剩余num= 2
4 Thread-2飞机票还剩余num= 1
5 Thread-0飞机票还剩余num= 3
6 Thread-0飞机票还剩余num= 2
7 Thread-0飞机票还剩余num= 1
8 Thread-1飞机票还剩余num= 2
9 Thread-1飞机票还剩余num= 1
View Code

    2)第二段:

 1 public class ThreadTest5 implements Runnable {
 2     private int num=3;//定义飞机票的张数
 3     public void run(){
 4         for(int i=0;i<10;i++){
 5             if(num>0){
 6                 System.out.println(Thread.currentThread().getName()+"飞机票还剩余num= "+num--);
 7             }
 8         }        
 9     }
10     public static void main(String[] args){
11         ThreadTest5 threadtest1=new ThreadTest5();
12         ThreadTest5 threadtest2=new ThreadTest5();
13         ThreadTest5 threadtest3=new ThreadTest5();
14         Thread thread1=new Thread(threadtest1,"窗口1");
15         Thread thread2=new Thread(threadtest2,"窗口2");
16         Thread thread3=new Thread(threadtest3,"窗口3");
17         thread1.start();
18         thread2.start();
19         thread3.start();
20     }
21 }
View Code

      结果:

1 窗口2飞机票还剩余num= 3
2 窗口2飞机票还剩余num= 2
3 窗口2飞机票还剩余num= 1
4 窗口1飞机票还剩余num= 3
5 窗口1飞机票还剩余num= 2
6 窗口1飞机票还剩余num= 1
7 窗口3飞机票还剩余num= 3
8 窗口3飞机票还剩余num= 2
9 窗口3飞机票还剩余num= 1
View Code

     3)第三段:

 1 public class ThreadTest7 implements Runnable {
 2     private int num=3;//定义飞机票的张数
 3     public void run(){
 4         for(int i=0;i<10;i++){
 5             if(num>0){
 6                 System.out.println(Thread.currentThread().getName()+"飞机票还剩余num= "+num--);
 7             }
 8         }        
 9     }
10     public static void main(String[] args){
11         ThreadTest7 threadtest1=new ThreadTest7();
12         Thread thread1=new Thread(threadtest1,"窗口1");
13         Thread thread2=new Thread(threadtest1,"窗口2");
14         Thread thread3=new Thread(threadtest1,"窗口3");
15         thread1.start();
16         thread2.start();
17         thread3.start();
18     }
19 }
View Code

      结果:

1 窗口1飞机票还剩余num= 3
2 窗口1飞机票还剩余num= 2
3 窗口1飞机票还剩余num= 1
View Code

    2. 分析:

      第一和第二段代码不管是使用Runnerable实现还是使用Thread实现,从结果可以看出三个线程每个线程均有num这个资源,如果把num看做是飞机票的话,那么每个线程都有三张飞机票。这不是我们实际中要得到的情况。

      要实现这个飞机售票程序,我们只能创建一个资源对象,但要创建多个线程去处理同一个资源对象,并且每个线程上所运行的是相同的程序代码。

      这就是第三段代码实现的结果,ThreadTest7只创建了一个资源对象——threadtest1。创建了三个线程,每个线程调用的是同一个threadtest1对象中的run()方法,访问的是同一个对象中的变量(num)的实例,这个程序满足了我们的需求。

     3. 结论:

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

【后面的话】

     写一篇文章的过程是充满了乐趣的一次奇妙的旅程,有时“柳暗“,有时”花明“。但是当写完的时候如同一次旅行的结束,总会是有收获的。

分享:

  1. 10层以下,走楼梯
  2. 左手刷牙
  3. 公交车读书
  4. 持续早起早睡
  5. 出门的时候记得,“伸手要纸钱“——身份证,手机,钥匙,纸巾,钱包。

 

——TT

posted @ 2014-02-20 17:30  赞恩  阅读(1625)  评论(6编辑  收藏  举报