进程和线程
从操作系统来讲,最早是DOC,传统的DOC是单进程的处理方式。而道理windows时代,采用是的多进程的处理方式。在同个时间段上会有多个程序并发执行,轮流抢占CPU资源。但是进程的启动和销毁是很缓慢的,后来在进程做进一步的优化,就产生了线程的概念,即:线程是在进程的基础之上扩充的。线程的启动和销毁比进程的更快。而java是为数不多的支持多线程编程的语言之一。
多线程的实现(重点)
如果要想实现多线程的开发,那么就必须像主类存在那样。也需要一个线程的主体类,但是这个类并不能单独定义,必须让其继承Thread类或者是实现Runnable接口继承Thread类
线程的主体类通过extends关键字继承Thread类,那么这个类就可以用于线程的控制,当继承了Thread类之后中用户还需要重写Thread类之中的run()方法“public void run()”。范例:定义线程的主体类:
class MyThread extends Thread{
private String name;//理解为对象的名字
public MyThread(String name) {
this.name=name;
}
@Override
public void run() {//线程的主方法
for(int x=0;x<10;x++) {
System.out.println(this.name+",x="+x);
}
}
}
此时每一个MyThread类的对象都是一个线程,那么可以产生多个线程。
但是当线程主体类定义完成后,必须通过对象进行访问,但是调用的并不是类中的run()方法,而是Thread的start()方法“public void start”,
调用此方法就相当于调用了run()方法。
范例:启动多线程
package thread;
class MyThread extends Thread{
private String name;
public MyThread(String name) {
this.name=name;
}
@Override
public void run() {//线程的主方法
for(int x=0;x<10;x++) {
System.out.println(this.name+",x="+x);
}
}
}
public class Threada {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread mtA=new MyThread("线程A");
MyThread mtB=new MyThread("线程B");
MyThread mtC=new MyThread("线程C");
mtA.start();
mtB.start();
mtC.start();
}
}
如果使用的是run()方法实际上只是一个方法的普通调用,将采用顺序的方式执行,而如果调用的是start()方法则表示将启动多线程
,多个线程彼此之间并发执行。
疑问:通过简短的分析,应该已经知道多线程的基本运行过程了,但是为什么要通过start()来调用run()昵?
打开关于Thread类中的start()方法的定义
public synchronized void start(){
if(threadstatus!=0)
throw new IllegalThreadStartException();
group.add(this);
boolean started=false;
try{
start0();
started=true;
}finally{
try{
if(!started){
group.threadStartFailed(this);
}
}catch(Throwable ignore){
}
}
}
private native void start0();
本方法会抛出一个“IllegalThreadStartException”异常,但是如果是手工的使用throw抛出异常,应该使用try..catch处理才对。但是此时并没有强制性要求,因为观察“IllegalThreadStartException”是RuntimeException的子类,一般当一个线程被重复启动的时候就会抛出此异常。所以一个线程只能够启动一次。
可以发现在调用start()方法的时候会自动调用start0()方法,而在start0()方法的声明处发现有一个native关键字,那么此关键字表示的是,将通过本地的原始函数进行代码的实现。“private native void start0();”
线程的操作一定是需要进行CPU资源调度,每个操作系统的资源调度是不一样的,所以此方法实际上会由JVM实现,那么编写的时候只给出了一个
抽象方法的名称,而具体的实现将根据不同的操作系统进行重写,所以才实现可移植性。
所以可以得出结论,当用户执行start()方法的时候意味着将进行操作系统的资源调配,调配之后才会执行run()方法,
即:任何时候只要是启动多线程,都一定要使用Thread类之中的start()方法。
实现Runnable接口
类与类之间的继承并不推荐取使用,但类实现接口是一个推荐的功能,在java里面为了解决多线程的单继承局限问题,所以也提供有一个实现Runnable接口,用户只需要让线程的主体类实现实现Runnable接口即可。
范例:观察实现Runnable接口的定义结构
public interface Runnable{
public void run();
}
范例:使用Runnable定义线程主体类
package thread;
class MyThread implements Rennable{
private String name;
public MyThread(String name) {
this.name=name;
}
@Override
public void run() {//线程的主方法
for(int x=0;x<10;x++) {
System.out.println(this.name+",x="+x);
}
}
}
通过之前的分析可以得出结论,只要是线程的启动一定要依靠Thread类的start()方法,如果所现在一个类直接继承了Thread类,那么可以继承下start()方法,但是此时实现的Runnable接口,那么怎么可以start()可以被继承?
观察Thread类中的构造方法的定义:“public Thread(Runnable target)”.发现可以接收一个Runnable接口子类对象
范例:启动多线程
package thread;
class MyThreada implements Runnable{
private String name;
public MyThreada(String name) {
this.name=name;
}
@Override
public void run() {//线程的主方法
for(int x=0;x<10;x++) {
System.out.println(this.name+",x="+x);
}
}
}
public class Threadb {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThreada mtA=new MyThreada("线程A");
MyThreada mtB=new MyThreada("线程B");
MyThreada mtC=new MyThreada("线程C");
new Thread(mtA).start();
new Thread(mtB).start();
new Thread(mtC).start();
}
}
此时两者的功能是完全相同的,但是很明显,使用Runnable接口比使用Thread类更加合理
两种实现方式的区别
我们已经清楚看多线程的两种实现方式,但是这两种方式从结构上讲一定使用的是Runnable接口,可是除了这一点之外,还有其它区别吗?那么首先来观察一下Thread类的定义:
public class Thread extends Object implements Runnanle
发现原来Thread类也实现了Runnable接口。
从结构上讲Thread是一个代理类的结构设计,但是又不那么完整。如果是一个纯粹的代理设计模式,那么用户应该调用的是Thread类的run()方法,但是现在调用的是start()方法(并不是Runnable接口提供的方法),所以在整个操作之中虽然形式是代理结构,但是最终还是有差异的。
还有一点小区别:使用Runnable接口实现的多线程要比使用Thread类的多线程更容易表示出数据共享的概念
范例:编写一个简单的买票程序,利用Thread类实现(产生三个线程)
package thread;
class MyThreadb extends Thread{
private int ticket=5;//假设有5张票
@Override
public void run() {//线程的主方法
for(int x=0;x<10;x++) {
if(this.ticket>0) {
System.out.println("卖票:ticket="+this.ticket--);
}
}
}
}
public class Threadc {
public static void main(String[] args) {
// TODO Auto-generated method stub
new MyThreadb().start();//一个线程
new MyThreadb().start();//二个线程
new MyThreadb().start();//三个线程
}
}
这个时候发现每一个线程对象都有各自的五张票进行售卖,不符合要求。
范例:利用Runnable实现
package thread;
class MyThreadc implements Runnable{
private int ticket=5;//假设有5张票
@Override
public void run() {//线程的主方法
for(int x=0;x<10;x++) {
if(this.ticket>0) {
System.out.println("卖票:ticket="+this.ticket--);
}
}
}
}
public class Threadd {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThreadc mt=new MyThreadc();
new Thread(mt).start();//一个线程
new Thread(mt).start();//二个线程
new Thread(mt).start();//三个线程
}
}
修改:Thread类实现
package thread;
class MyThreadb extends Thread{
private int ticket=5;//假设有5张票
@Override
public void run() {//线程的主方法
for(int x=0;x<10;x++) {
if(this.ticket>0) {
System.out.println("卖票:ticket="+this.ticket--);
}
}
}
}
public class Threadc {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThreadb mt=new MyThreadb();
new Thread(mt).start();//一个线程
new Thread(mt).start();//二个线程
new Thread(mt).start();//三个线程
}
}
面试题:请解释出多线程两种实质方式的区别?分别编写程序进行说明?
多线程的两种实现方式:继承Thread类,实现Runnable接口;
如果继承了Thread类,那么受到单继承局限,而且不方便表示出数据共享的概念,Thread类是Runnable接口的子类:
如果实现了Runnable接口,那么将不受到单继承的影响,同时可以方便的表示出数据出共享的操作
但是不管使用何种方式,最终一定要通过Thread类的start()方法才可以启动多线程。
上面的代码就是说明。
public class a {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}).start();
}
}
以上是匿名内部类进程和线程
从操作系统来讲,最早是DOC,传统的DOC是单进程的处理方式。而道理windows时代,采用是的多进程的处理方式。在同个时间段上会有多个
程序并发执行,轮流抢占CPU资源。
但是进程的启动和销毁是很缓慢的,后来在进程做进一步的优化,就产生了线程的概念,即:线程是在进程的基础之上扩充的。线程的启动
和销毁比进程的更快。而java是为数不多的支持多线程编程的语言之一。