Java拾贝第八天——多线程的创建
Java拾贝不建议作为0基础学习,都是本人想到什么写什么
进程与线程
多线程是实现并发机制的一种有效手段,进程和线程一样都属于并发的一个单位。(线程比进程更小)。
所谓多线程是指一个进程在执行过程中可以产生多个线程。这些线程可能同时存在、同时运行。
一个进程可以包含一个或多个线程,但至少会有一个线程。
一般把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另一个进程。
线程一般指程序的运行流程
多线程机制是指可以同时运行多个程序块。
线程的创建
在Java中创建线程一般有两种方式:继承Thread类,实现Runnable接口。
继承Thread类
必须继承Thread类并重写run()方法。
public class Test8 {
public static void main(String[] args) {
MyThread m1 = new MyThread("线程m1");
MyThread m2 = new MyThread("线程m2");
m1.run();
m2.run();
}
}
class MyThread extends Thread {//继承Thread
private String name;
public MyThread(String name) {//构造方法
this.name = name;
}
@Override
public void run() {//重写run方法
for (int i = 0; i < 10; i++) {
System.out.println(this.name + "=" + i);
}
}
}
程序运行结果:
线程m1=0
线程m1=1
线程m1=2
线程m1=3
线程m1=4
线程m1=5
线程m1=6
线程m1=7
线程m1=8
线程m1=9
线程m2=0
线程m2=1
线程m2=2
线程m2=3
线程m2=4
线程m2=5
线程m2=6
线程m2=7
线程m2=8
线程m2=9
从结果上看并没有交错运行,因为线程实际上压根没有启动。
上述写法只是普通地实例化了一个对象并调用其run方法。
正确应该使用父类的Thread类的start()方法。
修改代码:
public class Test8 {
public static void main(String[] args) {
MyThread m1 = new MyThread("线程m1");
MyThread m2 = new MyThread("线程m2");
m1.start();//调用start()方法
m2.start();
}
}
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {//run()就是线程执行的任务
for (int i = 0; i < 10; i++) {
System.out.println(this.name + "=" + i);
}
}
}
程序运行结果:
线程m2=0
线程m1=0
线程m2=1
线程m1=1
线程m2=2
线程m1=2
线程m2=3
线程m2=4
线程m2=5
线程m2=6
线程m2=7
线程m2=8
线程m2=9
线程m1=3
线程m1=4
线程m1=5
线程m1=6
线程m1=7
线程m1=8
线程m1=9
成功了!哪个线程对象抢到了CPU资源谁就运行。所以程序的每次运行结果肯定是不一样的。
如果一个线程对象多次调用start()方法,将会抛出IllegalThreadStateException异常。
public static void main(String[] args) {
Thread thread = new Thread() {//匿名内部类
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
};
thread.start();
thread.start();
}
程序运行结果:
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:710)
at moudle1.Test8.main(Test8.java:14)
0
1
2
3
4
5
6
7
8
9
实现Runnable接口
必须实现Runnable并重写run()方法。
但Runnable接口并没有提供默认的start(),那我们如何启动呢?
在Thread类中提供了构造方法,其传参就是Runnable接口的实现类
public Thread(Runnable target);
public class Test8 {
public static void main(String[] args) {
MyThread m1 = new MyThread("m1");
MyThread m2 = new MyThread("m2");
new Thread(m1).start();
new Thread(m2).start();
}
}
class MyThread implements Runnable {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.name + "=" + i);
}
}
}
程序运行结果:
m1=0
m2=0
m1=1
m2=1
m1=2
m2=2
m1=3
m2=3
m1=4
m2=4
m1=5
m2=5
m1=6
m2=6
m1=7
m2=7
m1=8
m2=8
m1=9
m2=9
从以上两种实现可以发现,无论使用哪种方式,最终都必须依靠Thread类才能启动线程。
注意:
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类形式线程1");
}
}).start();
System.out.println("main");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类形式线程2");
}
}).start();
}
程序运行结果:
main
匿名内部类形式线程1
匿名内部类形式线程2
虽然代码书写方式是从上至下的,但实际上这两个线程是平级并且不得知哪个线程优先启动。