2016.6.16笔记(1)-多线程(1)

早上没什么事,看了下多线程,做个笔记。

多线程的概念

  • 进程的概念:
    进程是操作系统资源管理的最小单位,进程是一个动态的实体,他是程序的一次执行过程。也叫作一个控制单元
  • 线程的概念:
    线程是进程中独立的控制单元,线程控制着进程的执行。一个进程中至少有一个线程。
  • java VM(java虚拟机)在运行时启动了一个进程—java.exe; 该进程在执行时,至少有一个线程在控制着java程序的运行,并且这个线程存在于java的main函数中,该线程称之为java的主线程。
  • 扩展:在JVM运行时,除了main函数中的线程在运行外,还有JVM中负责Java垃圾回收的线程在运行。因此,java不是单线程运行程序。

    线程的5个状态图(线程和进程一样,具有5个状态:创建、就绪、运行、阻塞、终止):
    线程的5个状态图

程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用MitiSay的两个对象的start方法,另外两个线程也启动了,这样,整个应用就在多线程下运行。

注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable)(应该就是就绪状态吧),什么时候运行是由操作系统决定的。

实现多线程的第一种方式(单继承方式):

  1. 继承自Thread类实现多线程
    步骤是:
    1) 创建一个类继承Thread;
    2) 覆写run方法,目的是存放多线程要执行的自定义代码;
    3) 在main函数中创建该类;
    4) 使用start()方法调用该线程(start方法有两种含义:1,启动多线程。2,调用线程中的run方法);

    public class Day1{  
     public static void main(String args[]){  
        DemoThread dt = new DemoThread();  
        dt.start();  //用于启动线程,并自动执行run方法。  
        dt.run(); //只是单纯的对象调用,在主线程中执行,并不开启子线程。  
        for(int i=0;i<50;i++){  
          System.out.println("main run-----"+i);  
        }  
     }  
    }  
    class DemoThread extends Thread{  
      public void run(){  
        for(int i=0;i<50;i++)  
          System.out.println("demo run-----"+i);  
        }  
    }  

我们发现运行同一个程序,他们的运行结果却不太相同,
这是多个线程都获取系统的CPU资源,CPU执行到谁,谁就运行
CPU在某一时刻只能执行一个程序(多核除外),CPU在做着快速的切换,以达到看上去是在同时执行的效果。
我们通过打印输出,来判断到底是哪一个线程抢占到了CPU的资源。

Thread类的意义—–用于描述线程。
该类定义了一个功能,用于存储线程要运行的代码,而这个存储功能就是run方法
run方法中存储线程要执行的 自定义代码块。
而start方法用于启动线程,并自动执行run方法。

Thread类中提供的常用的方法:
- static Thread currentThread: 返回当前正在执行的线程对象的引用
- String getName();返回当前线程的名称。
- 当使用Thread的无参构造创建线程实例时,java虚拟机会自动为线程创建一个名字。(以Thread-编号的格式).
- static void sleep(long time) 使线程休眠time时间。

创建线程的第二种方式(实现Runnable接口):

实现Runnable 接口来实现多线程:
步骤:
1) 创建类实现Runnable接口
2) 实现Runnable接口中的run方法
3) 创建Thread对象
4) 将Runnable对象作为实际参数传递给Thread的构造方法
5) 调用Thread类的start方法,自动执行Runnable对象中的run方法.

public class Day1Runable{  
 public static void main(String args[]){  
    DemoRunnable dr = new DemoRunnable();  
    Thread t1 = new Thread(dr);  
    Thread t2 = new Thread(dr);  
    Thread t3 = new Thread(dr);  
    Thread t4 = new Thread(dr);  
    t1.start();  
    t2.start();  
    t3.start();  
    t4.start();  
 }  
}  
class DemoRunnable implements Runnable{  
  private int ticket=100;  
  public void run(){  
    while(true){  
      if(ticket>0)  
        System.out.println(Thread.currentThread().getName()+"--售票:"+ticket--);  
      }  
    }  
}  

问题:继承方式与实现方式有什么区别

1、避免了单继承的局限性
单继承的局限性是指,只能有一个demothread类继承父类,而且,类之间的耦合性增加,父类的改动会直接影响子类。
Runnable则不一样呀,可以有多个demorunnable类实现Runnable类。
2、多线程执行代码位置不同:
- 继承Thread类:代码存放在Thread类的run方法中
- 实现Runnable类:代码存放在Runnable接口的run方法中。

问题

但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。

    Thread1 mTh1=new Thread1("A");
    Thread1 mTh2=mTh1;
    mTh1.start();
    mTh2.start();

Exception in thread “main” java.lang.IllegalThreadStateException
at java.lang.Thread.start(Unknown Source)
at com.multithread.learning.Main.main(Main.java:31)

本质

实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

Run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。
Thread类实际上也是实现了Runnable接口的类。在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。

举个例子,Thread和Runnable的区别

package com.multithread.learning;
/**
 *@functon 多线程学习,继承Thread,资源不能共享
 *@author 林炳文
 *@time 2015.3.9
 */
class Thread1 extends Thread{
    //这个count不是共享的
    private int count=5;
    private String name;
    public Thread1(String name) {
       this.name=name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "运行  count= " + count--);
            try {
                sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Main {

    public static void main(String[] args) {
        Thread1 mTh1=new Thread1("A");
        Thread1 mTh2=new Thread1("B");
        mTh1.start();
        mTh2.start();

    }

}

输出结果是:
B运行 count= 5
A运行 count= 5
B运行 count= 4
B运行 count= 3
B运行 count= 2
B运行 count= 1
A运行 count= 4
A运行 count= 3
A运行 count= 2
A运行 count= 1

上面可以看出,不同的线程之间count是不同的,这对于卖票系统来说就会有很大的问题,当然,这里可以用同步来作。这里我们用Runnable来做下看看:

/**
 *@functon 多线程学习 继承runnable,资源能共享
 *@author 林炳文
 *@time 2015.3.9
 */
package com.multithread.runnable;
class Thread2 implements Runnable{
    private int count=15;
    @Override
    public void run() {
          for (int i = 0; i < 5; i++) {
              System.out.println(Thread.currentThread().getName() + "运行  count= " + count--);
                try {
                    Thread.sleep((int) Math.random() * 10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

    }

}
public class Main {

    public static void main(String[] args) {

        Thread2 my = new Thread2();
            new Thread(my, "C").start();//同一个mt,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常   
            new Thread(my, "D").start();
            new Thread(my, "E").start();
    }

}

输出:
C运行 count= 15
D运行 count= 14
E运行 count= 13
D运行 count= 12
D运行 count= 10
D运行 count= 9
D运行 count= 8
C运行 count= 11
E运行 count= 12
C运行 count= 7
E运行 count= 6
C运行 count= 5
E运行 count= 4
C运行 count= 3
E运行 count= 2

这里要注意每个线程都是用同一个实例化对象,如果不是同一个,效果就和上面的一样了!

总结:

实现Runnable接口比继承Thread类所具有的优势:

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

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

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

提醒一下大家:main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实习在就是在操作系统中启动了一个进程

1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值