在对线程、多线程、进程、多进程的阐述之后,对自己的鼓励还是比较大的,因为在来到CSDN半年左右的时间,终于拿到了一次首页推荐的机会,但是自认为还是比较菜的,应该算是在各位前辈面前献丑了…,在后面的日子里自己也会倍加努力的,今天想继续分享一些线程相关的知识点,希望得到大家的批评指点,更希望可以帮助更多的伙伴们!
线程创建的两种方式
1.继承Thread类的方式
单个线程的创建:
Java中的线程有一个专门的类——Thread
,在Thread
类中会提供一个run方法,因此用户在使用线程的时候,主要是采用继承Thread
加覆盖run
方法的形式来实现所需的业务逻辑。
在run方法里仅仅只做了一个对象的非空判断,new对象后调用Thread
中的run方法而不去覆盖是没有意义的;
/* What will be run. */
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
多线程的创建:
在主函数中调用start()
方法时,旨在告诉主函数所有的线程都已经准备完毕,进而启动线程,需要CPU去给各个线程分配执行的时间片。因为main()函数时函数的起始位置,因此时间片最先在主线程,主线程时间片执行到期后,随机分配给其他线程。
方法 | 描述 |
---|---|
void start() | 使该线程开始执行; Java虚拟机调用此线程的run方法。 |
public class TestCreateThread {
public static void main(String[] args){
MyThread1 thread1 = new MyThread1();
MyThread2 thread2 = new MyThread2();
thread1.start();
thread2.start();
for (int i = 0; i < 50; i++) {
System.out.println("Main: "+i);
}
System.out.println("程序执行结束!");
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("MyTread1:"+i);
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("MyTread2:"+i);
}
}
}
打印结果:
- 分配时间片的对象是随机的,时间片的长度也是随机的;
- 最有可能先执行完毕的main函数
2.实现Runnable接口的形式
Runnable
是一个接口, 接口本身是一种能力,而Runnable
的能力就是使程序的线程开始执行,这主要因为此接口里面有一个run()
方法;因此任何一个线程,只要传入该接口实现类的实例对象为参数,那么,此线程就具备独立执行的能力;
Runnable
接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为run 。
- Runnable接口源码
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Thread
类部分源码
虽然Runnable
参数传进来之后,经过几次方法的调用和传递,就做了一件事情:当使用Thread
使用有参构造方法时,实际上就是把Runnable
类型的对象,最后付给了它的实例变量,类似于构造函数的形式;
/* What will be run. */
private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize,AccessControlContext acc,boolean inheritThreadLocals) {
//此处原则性筛除一些代码
this.target = target;
}
此时的Thread
中的run
方法中的判断,此时因Runnable
参数的传入,满足了target != null
的条件,因此调用 target.run();
方法,这里就是接口的回调,回调Runnable
中的run()
方法;
@Override
public void run() {
if (target != null) {
target.run();
}
}
单个线程的创建
多个线程的创建
public class TestRunnable {
public static void main(String[] args){
MyRunnable runnable = new MyRunnable();
//分别创建两个线程,并传入runnable参数
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
//运行线程
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
//打印当前线程的名字和值
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
打印结果:
小结
- 类比两种方式,方式二灵活性更强一些,尤其是多个线程执行相同的业务逻辑时,方式二的使用更具有合理性;
- 线程的状态执行基本过程: