创建线程的三种方式
1.继承Thread类 重写run方法
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ":打了" + i + "个小兵");
}
}
}
public class Test {
public static void main(String[] args) {
//创建MyThread对象
MyThread t1=new MyThread();
MyThread t2=new MyThread();
MyThread t3=new MyThread();
//设置线程的名字
t1.setName("鲁班");
t2.setName("刘备");
t3.setName("亚瑟");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
2.实现eunnable 重写run方法
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
try {//sleep会发生异常要显示处理
Thread.sleep(20);//暂停20毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "打了:" + i + "个小兵");
}
}
}
public class Test {
public static void main(String[] args) {
//创建MyRunnable类
MyRunnable mr = new MyRunnable();
//创建Thread类的有参构造,并设置线程名
Thread t1 = new Thread(mr, "张飞");
Thread t2 = new Thread(mr, "貂蝉");
Thread t3 = new Thread(mr, "吕布");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
class CallerTask implements Callable<String> {
public String call() throws Exception {
return "Hello,i am running!";
}
public static void main(String[] args) {
//创建异步任务
FutureTask<String> task=new FutureTask<String>(new CallerTask());
//启动线程
new Thread(task).start();
try {
//等待执行完成,并获取返回结果
String result=task.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
注意!!:以上两种方式都是要调用start方法,才能真正实现多线程。直接调用run相当于调用普通方法
线程状态
// Thread.State 源码
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
NEW状态
表示线程已经被创建,但是还未被启动
测试在start之前调用getState查看状态,是NEW
Thread t1 = new Thread(mr, "张飞");
Thread t2 = new Thread(mr, "貂蝉");
Thread t3 = new Thread(mr, "吕布");
System.out.println(t1.getState());
//启动线程
t1.run();
Runnable状态
表示当前线程正在运行中。处于 RUNNABLE 状态的线程在 Java 虚拟机中运行,也有可能在等待 CPU 分配资源。
在操作系统层面,线程状态包括ready和running,但是在JVM层面,只能看到Runnable状态,也就是将这两种状态统称为Runnable(运行中)。
Runnable状态可以由NEW状态调用start方法转变过来,但是:
- 反复调用同一个线程的 start 方法不可行,回抛出异常
- 假如一个线程执行完毕(此时处于 TERMINATED 状态),再次调用这个线程的 start 方法也不可行
看下面的start代码,每次进入会判断threadStatus变量,这个变量代表当前线程的状态。如果线程已经启动或者已经终止,threadStatus就不等于0,进入start就会 throw IllegalThreadStateException();
// 使用synchronized关键字保证这个方法是线程安全的 public synchronized void start() { // threadStatus != 0 表示这个线程已经被启动过或已经结束了 // 如果试图再次启动这个线程,就会抛出IllegalThreadStateException异常 if (threadStatus != 0) throw new IllegalThreadStateException(); // 将这个线程添加到当前线程的线程组中 group.add(this); // 声明一个变量,用于记录线程是否启动成功 boolean started = false; try { // 使用native方法启动这个线程 start0(); // 如果没有抛出异常,那么started被设为true,表示线程启动成功 started = true; } finally { // 在finally语句块中,无论try语句块中的代码是否抛出异常,都会执行 try { // 如果线程没有启动成功,就从线程组中移除这个线程 if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { // 如果在移除线程的过程中发生了异常,我们选择忽略这个异常 } } }
BLOCK
阻塞状态。处于 BLOCKED 状态的线程正等待锁的释放以进入同步区。
WAITING
等待状态。处于等待状态的线程变成 RUNNABLE 状态需要其他线程唤醒。
BLOCK和WAITING有哈区别?
TIMED_WAITING
TIMED_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)方法或 wait(long millis)方法可以将线程置于 TIMED_WAITING 状态。当超时时间结束后,线程将会返回到 RUNNABLE 状态。
线程状态的转变