线程

线程和进程的概念:

其实在电脑上打开一个应用程序的时候就开启了一个进程,比如果我们在打开播放器在线观看电视剧的时候,此时就开启了一个进程,可以一边播放一边缓存下载,播放是一个线程完成的,缓存是另外一个线程完成的,那么我们可以认为进程是线程的载体,一个进程由多个线程组成,线程是进程最小单位

在Java中我们第一次输出Hello world的使用在一个类中定义主方法实现的,这个主方法的运行就是启动一个线程,这个线程叫做主线程,这个主线程的入口就是方法名“main(String[] args)”,如果方法名称不是main()则无法启动这个主线程。

可以在主线程中继续启动新的线程,这样的线程相对于主线程来说他们叫做子线程。在Java中有三个种方式可以实现线程的创建。

继承Thread类:只要继承了这个类的子类都可以创建线程(不常用,因为有单继承局限,而且不方便实现数据共享)

实现Runnable接口:实现了Runnable接口的子类也可以创建线程(最常用的方法,因为没有单继承局限还可以方便实现数据的共享)

实现Callable接口:这个接口是在jdk1.5版本增加(基本上不使用)

Thread类实现线程的创建:

继承了Thread类的子类就是一个线程主体类。我们知道主线程入口是main()方法,那么其他也一样需要有自己的入口方法才能启动,其他线程的入口方法是start()和run()

创建一个线程主体类:

package com.sun.thread;
public class MyThread  extends   Thread {
	private  String threadName;
    public MyThread() {
	}
    public MyThread(String  threadName) {
    	this.threadName=threadName;
    }
	@Override
	public void run() {
		System.out.println(this.threadName+"启动了");
	}
}

以上就定义了一个线程主体类,之后需要使用该类创建线程对象启动。

创建线程对象:

package com.sun.thread;
public class ThreadTest {
   public static void main(String[] args) {
	  //创建线程对象
	   Thread   th1=new MyThread("线程A");
	   Thread   th2=new MyThread("线程B");
	   Thread   th3=new MyThread("线程C");
	   Thread   th4=new MyThread("线程D");
	   Thread   th5=new MyThread("线程E");
   }
}

上面在主方法中创建了线程对象,那么一个线程对象需要启动才有意义,如何启动一个线程,我们暂时使用run()方法启动

使用run()方法作为入口启动线程:

public class Test {
	public static void main(String[] args){
		//创建线程对象 并且启动线程
		Thread  thread1=new  MyThread("线程A");
		Thread  thread2=new  MyThread("线程B");
		Thread  thread3=new  MyThread("线程C");
		Thread  thread4=new  MyThread("线程D");
		//调用线程对象的run()方法启动
		thread1.run();
		thread2.run();
		thread3.run();
		thread4.run();
	}
}

在这里插入图片描述

发现了以上启动线程的顺序就是代码的顺序,这样不符合多线程的并发性,随机性,正常来说线程的启动是随机的,不会有固定顺序的,而是每次都不一样,出现DEMO中的根本原因没有启动线程,使用run()仅仅是调用了Java类中的一个普通的方法,所以线程没有启动。如果要真正的启动线程需要使用start()方法

使用start()方法启动线程:

public class Test {
	public static void main(String[] args){
		//创建线程对象 并且启动线程
		Thread  threadA=new  MyThread("线程A");
		Thread  threadB=new  MyThread("线程B");
		Thread  threadC=new  MyThread("线程C");
		Thread  threadD=new  MyThread("线程D");
		//调用线程对象的run()方法启动
		threadA.start();
		threadB.start();
		threadC.start();
		threadD.start();
	}
}

在这里插入图片描述
当使用start()方法启动线程的时候会进行cpu资源的调度,为每个线程分配cpu资源,或者更确切的说是每个线程抢占cpu资源,谁先抢到资源就先执行谁。那么为什么会分配cpu资源呢?这个资源分配的任务是谁完成的呢?是本地系统的一些C函数完成的。也就是说当使用start方法之后jvm会调用本地系统的c函数。观察原码

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)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);
        boolean started = false;
        try {
            start0();
             started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    private native void start0();

从原码中可以看到start方法调用了start0方法,这个方法的定义是
“private native void start0();”该方法没有方法体,而且使用native修饰的,是一个本地方法,为什么叫做本地方法呢?因为该方法的实现是本地系统的函数去完成的(C语言写的函数)。调用这个方法的作用是进行cpu资源的调度。当分配到资源之后再回去调用run()方法

总结:

1、线程和进程的关系:进程是线程的载体,线程是进程的组成单位

2、线程的实现方法是有三种(Thread类、Runnable接口、Callable接口)

3、启动线程要使用start()方法才能真正的实现cpu资源的分配

使用Runnable接口实现线程:

在上面使用继承Thread类的方式实现了线程对象的创建以及启动,但是存在单继承的局限,还可以使用实现Runnable接口的方式创建和启动线程,而且避开了单继承的局限性。

使用Runnable接口

package com.sun.thread;
public class RunThread   implements  Runnable {
	 private  String threadName;
    public MyThread() {
	 }
    public MyThread(String  threadName) {
    	this.threadName=threadName;
    }
	@Override
	public void run() {
		System.out.println(this.threadName+"启动了");
	}
}

此时使用Runnable接口创建线程主体类,但是如何使用这个类去创建线程对象呢?

创建线程对象

package com.sun.test;
class  RunThread  implements  Runnable{
	private  String  threadName;
	public RunThread() {
	}
	public RunThread(String  threadName) {
		this.threadName=threadName;
	}
	@Override
	public void run() {
		 System.out.println(this.threadName+" 启动了!");
	}
}
public class Test {
	public static void main(String[] args){
		//创建线程对象 并且启动线程
		Runnable  threadA=new  RunThread("线程A");
		Runnable  threadB=new  RunThread("线程B");
		Runnable  threadC=new  RunThread("线程C");
		//调用线程对象的run()方法启动
	}
}

在这里插入图片描述
很遗憾的是Runnable接口中没有start方法启动线程,此时还得借助于Thread类辅助完成。Thread提供了一个构造方法可以接收Runnable类型的对象。

public Thread(Runnable target)

之后可以将Runnable对象传递给Thread的构造方法然后辅助启动线程。

借助Thread类的start()启动线程

public class Test {
	public static void main(String[] args){
		//创建线程对象 并且启动线程
		Thread  thA=new Thread(new  RunThread("线程A"));
		Thread  thB=new Thread(new  RunThread("线程B"));
		Thread  thC=new Thread(new  RunThread("线程C"));
		//调用线程对象的run()方法启动
		thA.start();
		thB.start();
		thC.start();
	}
}

在这里插入图片描述

面试题:

start()方法和run()方法的区别?

run(): 只是Java类的一个普通方法,不能实现cpu资源的调度,不能真正的启动一个线程

start(): 会启动一个线程,会在该方法中调用一个叫做start0()的方法进行cpu资源的调度,之后再去调用run()方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值