zoukankan      html  css  js  c++  java
  • java中的多线程入门

    本文主要是想学习下java中多线程的东西。

    一、理解多线程

    多线程是怎么样的机制?他是允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此之间相互独立。

    线程又称为轻量级进程,它和进程一样拥有独立的执行控制,有操作系统负责调度,区别在于线程诶有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

    多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”,如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中区别,所以不需要关心它。

    二、java中多线程的实现

    具体到java内存模型,由于java被设计为跨平台的语言,在内存管理上,显然也要有一个统一的模型。系统存在一个主内存,java中所有变量都存储在主存中,对所有线程都是共享的,每条线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行的,线程之间无法相互直接访问,变量传递均需要通过主存完成。

    作为一个完全面向对象的语言,java提供了类java.lang.Thread来方便多线程编程。这个类提供了大量方法来方便我们控制自己的各个线程。Tread类最重要的方法是run(),它为Thread类的方法start()所调用,提供我们的线程所要执行的代码。

    方法一:继承Thread类,覆盖run()方法

    我们在创建的Thread类的子类中重写run()方法,加入线程所要执行的代码即可。如下:

     1 public class MyThread extends Thread{
     2     
     3     int count = 1, number;
     4     public MyThread(int num){
     5         number = num;
     6         System.out.println("创建线程:" + number);
     7     }
     8     
     9     public void run(){
    10         while(true){
    11             System.out.println("线程" + number + ":计数  "+ count);
    12             if(++count == 6)
    13                 return ;
    14         }
    15     }
    16     
    17     public static void main(String[] args) {
    18         for(int i = 0; i < 5; i++){
    19             new MyThread(i+1).start();
    20         }
    21     }
    22 }

    结果如下:

    创建线程:1
    创建线程:2
    线程1:计数  1
    线程1:计数  2
    线程1:计数  3
    线程1:计数  4
    线程1:计数  5
    线程2:计数  1
    线程2:计数  2
    线程2:计数  3
    线程2:计数  4
    创建线程:3
    线程2:计数  5
    创建线程:4
    线程3:计数  1
    线程3:计数  2
    线程3:计数  3
    线程3:计数  4
    线程3:计数  5
    创建线程:5
    线程4:计数  1
    线程4:计数  2
    线程4:计数  3
    线程4:计数  4
    线程4:计数  5
    线程5:计数  1
    线程5:计数  2
    线程5:计数  3
    线程5:计数  4
    线程5:计数  5

     这里有一点需要注意,在main函数中,用到的是start函数,而不是run函数,大家可以试一下,用run函数得到的是顺序输出。为什么会用start而不是run?有人说在调用start函数的时候会首先进行与多线程相关的初始化,然后再调用run函数,也有人说通过start的源码

    public synchronized void start() {
            /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
             */
            if (threadStatus != 0 || this != me)
                throw new IllegalThreadStateException();
            group.add(this);
            start0();
            if (stopBeforeStart) {
            stop0(throwableFromStop);
        }
    }
    private native void start0();

    可以看出调用的是start0(),这个方法用了关键字native,这关键字表示调用本地操作系统的函数,因此需要用start()函数。因为多线程实现需要本地操作系统的支持。当调用run的时候系统并没有初始化多线程环境,还是在一个线程中。或者可以说,start是创建并启动一个线程,run只是运行其线程中代码。

    这种方法简单明了,符合大家习惯,但是也有一个很大的缺点,那就是如果我们的类已经从一个类继承,则无法再继承Thread类,这时我们又不想建立一个新的类,那咋写?如下。

    方法二:实现Runnable接口。

    Runnable接口只有一个方法run(),我们声明自己的类实现Runnable接口,接口提供这一个方法,将我们的线程代码写入其中,就完成这一部分任务。

    但是Runnable接口并没有任何对线程的支持,我们还必须创建Thread类的实例,这一点通过Thread类的构造函数public Thread(Runnable target)来实现。

    下面是一个例子:

     1 public class MyThread implements Runnable{
     2     int count = 1, number;
     3     public MyThread(int num){
     4         number = num;
     5         System.out.println("创建线程"+number);
     6     }
     7     public void run(){
     8         while(true){
     9             System.out.println("线程"+number+":计数:"+count);
    10             if(++count==6)
    11                 return ;
    12         }
    13     }
    14     public static void main(String[] args){
    15         for(int i = 0; i<5; i++){
    16             new Thread(new MyThread(i+1)).start();
    17         }
    18     }
    19 }

    结果如下:

    创建线程1
    创建线程2
    线程1:计数:1
    线程1:计数:2
    线程1:计数:3
    创建线程3
    线程2:计数:1
    线程1:计数:4
    线程2:计数:2
    线程1:计数:5
    创建线程4
    线程2:计数:3
    线程3:计数:1
    线程3:计数:2
    线程2:计数:4
    线程3:计数:3
    创建线程5
    线程3:计数:4
    线程4:计数:1
    线程4:计数:2
    线程4:计数:3
    线程4:计数:4
    线程5:计数:1
    线程2:计数:5
    线程5:计数:2
    线程4:计数:5
    线程3:计数:5
    线程5:计数:3
    线程5:计数:4
    线程5:计数:5

    严格的说,创建Thread子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖Thread类的run方法,否则该线程执行的将是子类的run方法而不是我们用以实现Runnable接口类的run方法。

    使用Runnable接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,他的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则必须额外创建类,这样的话就不如直接用多个类分别继承Thread来的紧凑。

    那么如何选择Runnable和Thread呢?

    其实Thread也是实现Runnable的,Thread和Runnable都实现了run方法。

    Thread和Runnable的区别

    如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。  

     1 class hello extends Thread {
     2     public void run() {
     3         for (int i = 0; i < 7; i++) {
     4             if (count > 0) {
     5                 System.out.println("count= " + count--);
     6             }
     7         }
     8     }
     9  
    10     public static void main(String[] args) {
    11         hello h1 = new hello();
    12         hello h2 = new hello();
    13         hello h3 = new hello();
    14         h1.start();
    15         h2.start();
    16         h3.start();
    17     }
    18  
    19     private int count = 5;
    20 }

    结果如下

    count= 5
    count= 4
    count= 5
    count= 4
    count= 5
    count= 3
    count= 2
    count= 3
    count= 1
    count= 4
    count= 2
    count= 1
    count= 3
    count= 2
    count= 1

    这里很明显看出并未实现资源(count)的共享,换位Runnable

     1 class MyThread implements Runnable{
     2  
     3     private int ticket = 5;  //5张票
     4  
     5     public void run() {
     6         for (int i=0; i<=20; i++) {
     7             if (this.ticket > 0) {
     8                 System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--);
     9             }
    10         }
    11     }
    12 }
    13 public class lzwCode {
    14      
    15     public static void main(String [] args) {
    16         MyThread my = new MyThread();
    17         new Thread(my, "1号窗口").start();
    18         new Thread(my, "2号窗口").start();
    19         new Thread(my, "3号窗口").start();
    20     }
    21 }

    结果如下

    2号窗口正在卖票5
    2号窗口正在卖票2
    2号窗口正在卖票1
    3号窗口正在卖票4
    1号窗口正在卖票3

    总结如下:

    实现Runnable接口比继承Thread接口有优势:

    1.适合多个相同的程序代码的线程去处理同一个资源

    2.可以避免java中的单继承的限制

    3.增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

    总之,对于多线程,虽然我用的不多,但是对于找工作或者啥的还是非常有用处的。这只是基本的多线程问题,以后还要深入研究多线程的细节东西。这算是入门吧。

  • 相关阅读:
    ZOJ
    Clock(数学题)
    The Lucky Week(规律)
    POJ 3233 Matrix Power Series
    POJ 1061 青蛙的约会(扩展欧几里德算法)
    2266: number
    2263: neighbor
    2269: minval(优先队列)
    HDU
    Problem 2150 Fire Game (广搜+枚举)
  • 原文地址:https://www.cnblogs.com/Pillar/p/4218756.html
Copyright © 2011-2022 走看看