首页 > 技术文章 > java-多线程概述

javastudy123 2016-05-13 08:50 原文

先理解一些概念

    进程(process)常常被定义为程序的执行。 可以把一个进程看成是一个独立的程序,在内存中有其完备的数据空间和代码空间。一个进程所拥有的数据和变量只属于它自己。 

    线程(thread)则是进程中一个单独运行的程序,也就是说,线程是存在与进程之中的。

 

    一个进程由一个或者多个线程构成,各个线程共享相同的代码和全局数据,但是每个线程都有自己独立的堆栈。

    由于每个线程都有一个堆栈,所以局部变量对每个线程来说是私有的。

    由于线程共享相同的代码和全局数据,线程比进程更紧密,比进程更趋向与相互作用,进程之间的通信通过全局数据。

 

    一个进程和一个线程最显著的区别:是线程有自己的全局数据。线程存在于进程中,因此一个进程的全局变量由所有的线程共享。由于线程共享同样的系统区域,操作系统分配给一个进程的资源对该进程的所有线程都是可用的,正如全局数据可供所有线程使用一样

 

    多线程:当有多部分代码需要同时运行时,就需要开辟多条执行路径来完成。

这时该程序就是多线程程序。

多线程解决了,让多部分代码同时运行的问题。开多了反而减低效率。

 

JVM中多线程

JVM中的多线程了解:

JVM中也一样是多线程程序,

只要有一个线程负责着程序的执行。

又有一个线程负责着垃圾的回收。

这个是同时进行的。

 

结论:

     每一个线程都有自己的运行代码,这个称之为线程的任务。

 

对于每一个线程便于识别都有自己的名称。

    比如负责从主函数执行程序代码的线程,称之为 主线程。main thread.

    

    主线程运行的代码都定义在主函数中。

    

    负责收垃圾的线程:垃圾回收线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    class Test extends Object {
        /*
        重写了垃圾回收器调用的方法
        */
        public void finalize()
        {
            System.out.println("test ok");
        }
    }
      
    class Demo{
        public static void main(String[] args) {
        new Test();
        new Test();
        new Test();
        new Test();
        new Test();
        System.gc(); //运行垃圾回收器
        System.out.println("Hello World!1");
        System.out.println("Hello World!2");
        System.out.println("Hello World!3");
        System.out.println("Hello World!4");
          
        }
    }
    //调用了一次垃圾回收器。主线程是不执行new Test();主函数在没有调用System.gc();时的时候,只看到Hello World!,调用之后Hello World和test ok是随机排列,

 

创建线程方式一-继承Thread

继承Thread类

1.子类覆盖父类中的run方法,将线程运行的代码存放在run中。

2.建立子类对象的同时,线程也被创建。

3.通过调用start方法开启线程。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Demo extends Thread{
    private String name;
    Demo(String name){
        this.name = name;}
         
    public void run(){
        for(int x=0; x<10; x++){
            System.out.println("name...."+x+"....."+name);
        }
    }
}
  
class ThreadDemo {
    public static void main(String[] args) {
        //创建线程对象。
        Demo d1 = new Demo("小强");
        Demo d2 = new Demo("旺财");
  
        //开启线程。让线程运行起来。
        d1.start();
        d2.start();
  
        d1.run();
        d2.run();
    }
}

调用run方法和调用start方法的区别?

调用run方法,从始至终都是主线程在运行。

调用start方法,是开启新的线程,新的线程运行run方法。

 

创建线程方式一  详解

 

每一个线程都应该有自己的任务,而且任务都会定义在指定的位置上。

 

    主线程的任务都定义在main方法中。

    自定义线程的任务都定义在了run方法中。

 

    Thread t = new Thread();

    t.start();

//这种开启只能调用Thread类中自己的run方法。而该run方法中并未定义自定义的内容。

 

    我还需要创建线程,还要让线程执行自定义的任务。

    所以可以复写run方法。前提必须是继承Thread类。

    而继承了Thread后,该子类对象就是线程对象。 

 

多线程-状态图解

wKioL1c0hVzym6-TAAE4nI1sDK4755.bmp

 临时阻塞状态是cpu并行执行多线程的时候,正在执行一种线程的时候,其他线程临时等待cpu。

sleep方法需要指定睡眠时间,单位是毫秒。sleep的时间结束的时候,就是冻结结束的时候。

Wait方法冻结线程之后是需要用notify方法唤醒。

从冻结状态恢复执行状态的线程可能会处于临时阻塞状态。

 

 

多线程-售票的例子

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
需求:通过4个窗口将100票卖出。
*/
class Ticket extends Thread{
    private int num = 100;
    Public void run(){
            sale();}
      
    public void sale(){
        while(true){
            if(num>0){   
                System.out.println(Thread.currentThread().getName()+"..sale:"+num--);
            }
        }
    }
}
  
class ThreadDemo3_Ticket {
    public static void main(String[] args) {
    //创建四个线程。
    Ticket t1 = new Ticket();
      
    //开启线程。
    t1.start();
    t1.start();
    t1.start();
    t1.start();
    //多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。那该怎么办,下面介绍创建线程的第二种方式。
    }
}

 

 

创建线程的第二种方式-实现Runnable接口

 

创建线程的第二种方式。实现Runnable接口。

1,实现Runnable接口。

2,覆盖run方法。

3,通过Thread类创建线程对象。

4,将Runnable接口的子类对象作为实参传递给Thread类中的构造函数。

因为要让线程去运行指定的对象的run方法。

5,调用start方法开启线程,并运行Runnable接口子类的run方法。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Ticket implements Runnable  {
    private int num = 100;
    public void run(){  //线程任务
        while(true){
            if(num>0){
                System.out.println(Thread.currentThread().getName()+"..sale:"+num--);
            }
        }
    }
}
  
  
class ThreadDemo3_Ticket_Runnable{
    public static void main(String[] args) {
        Ticket t = new Ticket();  //t 是任务对象
  
        Thread t1 = new Thread(t);  //t1,t2,t3,t4 是线程对象
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    //多个线程对象调用同一个线程任务
    }
}

 

创建线程的第二种方式-实现Runnable接口原理

Thread里面是如何使用Runnable接口,其他程序是如何实现这个接口?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//下面是Thread里面的部分代码。
class Thread{
    private Runnable target;
    Thread(){   }
    Thread(Runnable target){
        this.target = target;
    }
  
    public void run(){
        if(target!=null){
            target.run();
        }
    }
 
    public void start(){
        run();}
}
  
  
class Student implements Runnable{
    public void run(){  }
}
  
class Demo{
    public static void main(String[] args) { 
        Student stu = new Student();
        Thread t = new Thread(stu);
        t.start();}
}

 

创建线程的第二种方式-实现Runnable接口好处,这部分如果还是不理解的话可以去多看一些Java视频教程

第二种实现Runnable接口创建线程思想:

 

线程任务和线程对象进行解耦,将线程任务单独封装成对象。

 

另外,实现Runnable接口可以避免单继承的局限性。

 

所以建议创建多线程,都是用实现Runnable接口的方式。

推荐阅读