博文仅为学习记录,仅供参考
Java-多线程
1.概念
进程:正在运行的程序
线程:进程中的单个顺序控制流,是一条执行路径。
- 单线程:一个进程只有一条执行路径
- 多线程:一个进程有多条执行路径
2.实现多线程
Thread
类:public class Thread extends Object implements Runnable
继承关系:
java.lang.Object
java.lang.Thread
jdk文档中的说明:
线程是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在一些线程中运行的代码创建一个新的
Thread
对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某个指定类的名称为
main
的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:
- 该exit类的方法Runtime已经被调用并且安全管理器允许退出操作发生。
- 不是守护进程线程的所有线程都已经死亡,无论是从调用返回到run方法还是抛出异常,传播超出了run方法。
创建一个新的执行线程有两种方法。 一个是将一个类声明为一个
Thread
的子类。 这个子类应该重写Thread类的方法run()。 然后可以分配并启动子类的实例。创建线程的另一种方法是声明一个实现Runnable接口的类,然后那个类实现了run方法。
构造方法:
public Thread()
:分配一个新的线程对象public Thread(String name)
:分配一个指定名称的新线程对象public Thread(Runnable target)
:分配一个带指定目标的新线程对象public Thread(Runnable target,String name)
:分配一个带指定目标、指定名称的新线程对象
常用方法:
public String getName()
:获取当前线程名称public void run()
:此线程要执行的任务在这里面定义public void start()
:让此线程开始执行,Java虚拟机调用此线程的run()方法public static void sleep(long mills)
:使当前正在执行的线程以指定的毫秒数暂停public static Thread currentThread()
:返回对当前正在执行的线程对象的引用
先使用继承Thread类的方法实现多线程:
MyThread类:
public class MyThread extends Thread{
//重写run()方法
@Override
public void run() {
for(int i=0;i<100;i++){
//Thread类中有两个方法:getName()和setName(),用来获取和设置进程名称
System.out.println(this.getName()+":"+i);
}
}
}
测试类Main:
public class Main {
public static void main(String[] args) {
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
//设置名称
mt1.setName("线程a");
mt2.setName("线程b");
mt1.start();
mt2.start();
}
}
编译运行,控制台输出:
线程a:0
线程a:1
线程a:2
线程a:3
线程a:4
线程a:5
线程b:0
线程b:1
线程a:6
线程b:2
线程a:7
线程a:8
线程a:9
线程b:3
……
(上面省略号是略过后面的输出)
在MyThread类中重写了run()方法,在里面将整数1-100打印到控制台,在main()方法里面创建MyThread实例mt1和mt2,并分别设置它们的名称,然后分别执行mt1.start()和mt2.start(),start()这个方法就是启动一个新的线程,并执行这个对象里面的run()方法。从输出可以看出,虽然在main()里面是先执行mt1.start()然后再执行mt2.start(),但是并不是等mt1的run()执行完毕再执行mt2的run(),而是两个run()方法是没有规律地交错执行的。
多线程的意义:不同的线程在不同的栈空间,它们的执行互不影响。
下面使用第二种方式实现多线程:实现Runnable
接口。
步骤:
-
创建一个类实现Runnable接口
-
在这个类中重写run()方法,设置线程任务
-
创建类的对象
-
创建Thread类对象,在构造方法中将实现类的对象引用传进去
-
调用Thread类对象的start()方法
范例:
MyThread01类:
public class MyThread01 implements Runnable{
@Override
public void run() {
for (int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
TestMain类:
public class TestMain {
public static void main(String[] args) throws InterruptedException {
//创建Runnable接口的实现类对象,命名为mt
MyThread01 mt = new MyThread01();
//创建Thread类对象,将mt作为构造方法的参数
Thread t = new Thread(mt);
//执行t的start()方法
t.start();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
编译运行,控制台输出:
main:0
main:1
main:2
Thread-0:0
main:3
Thread-0:1
main:4
Thread-0:2
main:5
Thread-0:3
main:6
……
(上面省略号是略过后面的输出)
3.两种实现多线程的方式的区别
相比之下,使用实现Runnable接口的方式来创建多线程程序是比使用直接继承Thread类的方式有更多的优势:
- 避免了单继承的局限性:一个类只能继承一个父类,如果一个类继承了Thread类就不能再继承其他的类,如果使用实现Runnable接口的方式,则可以继承其他的类。
- 增强了程序的扩展性,降低了程序的耦合性:使用实现Runnable接口的方式,将设置线程任务和开启新线程进行分离(解耦),在实现类中重写run()方法,用来设置线程任务;创建Thread类对象,调用start方法,用来开启新的线程。
4.匿名内部类实现多线程
范例:
public class Main01 {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
this.setName("线程1");
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}.start();
Runnable r=new Runnable(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
};
new Thread(r,"线程2").start();
}
}
编译运行,控制台输出:
线程1:0
线程1:1
线程2:0
线程2:1
线程2:2
线程1:2
线程2:3
线程1:3
线程2:4
……
(上面的“……”为后面输出的省略)
使用匿名内部类创建对象实现多线程的好处是:方便创建不同的线程来执行不同的任务,简化代码。
博文仅为学习记录,仅供参考