您的位置:首页 > 编程语言 > Java开发

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

2016-06-17 15:45 537 查看
早上没什么事,看了下多线程,做个笔记。

多线程的概念

进程的概念:

进程是操作系统资源管理的最小单位,进程是一个动态的实体,他是程序的一次执行过程。也叫作一个控制单元

线程的概念:

线程是进程中独立的控制单元,线程控制着进程的执行。一个进程中至少有一个线程。

java VM(java虚拟机)在运行时启动了一个进程—java.exe; 该进程在执行时,至少有一个线程在控制着java程序的运行,并且这个线程存在于java的main函数中,该线程称之为java的主线程。

扩展:在JVM运行时,除了main函数中的线程在运行外,还有JVM中负责Java垃圾回收的线程在运行。因此,java不是单线程运行程序。

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



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

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

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

继承自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类还是实现Ru
4000
nnable接口来实现多线程,最终还是通过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()方法,该线程结束生命周期。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  多线程 java