Java多线程入门(1)
假如你没有一点Java多线程的基础,可以花10分钟看看我之前在知乎写的关于多线程的知识点:多线程简单入门
线程概述
多线程,简单来说就是多个线程(哈哈哈哈哈啊)。当我们使用计算的时候,可以同时打开QQ音乐和QQ和微信。这几个程序可以理解为多个进程,而一个进程又可以存在多个线程。实际应用中,对线程是非常游泳的,一个浏览器必须能同时下载多个照片;一个web服务器必须能同时响应多个用户请求。
线程的创建和启动
继承Thread类创建线程类
直接继承Thread,然后调用线程对象的start()方法来启动该线程。
直接上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class FirstThread extends Thread{ private int i; public void run() { for(;i<100;i++) { System.out.println(getName()+":"+i); } } public static void main(String[] args) { for(int i =0;i<100;i++) { System.out.println(Thread.currentThread().getName()+":"+i); if(i == 20) { new FirstThread().start(); new FirstThread().start(); } } }
}
|
实现Runnable接口创建线程类
定义Runnable接口得实现类,并重写该接口的run方法,这个方法就是该线程的线程执行体,最后还是调用线程对象的start方法来开启线程。注意实现Runnable的类本身并不能调用start方法,而是需要new Thread(Runable runable)来开启。使用同一个接口开启的线程会共享这个接口中的变量。
下面程序中可以看到线程1和线程2的 i 值是共享的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class SecondThread implements Runnable{ private int i;
@Override public void run() { for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String[] args) { for(int i =0;i<100;i++) { System.out.println(Thread.currentThread().getName()+":"+i); if(i == 20) { SecondThread st = new SecondThread(); SecondThread st1 = new SecondThread(); new Thread(st,"新线程1").start(); new Thread(st1,"新线程2").start(); } } } }
|
使用Callable接口来创建线程
这个类提供了call方法来作为线程执行体,但是call方法可以有返回值,且可以声明抛出异常。不过Callable不是Runnable接口的子接口,所以不能用上面的方法启动。Java5提供了Future接口来代表Callable接口的返回值,FutureTask实现类实现了Future接口,并实现了Runable接口,所以可以用这个实现类作为Thread类的内容。
该接口所提供的几个方法:
| 方法 |
描述 |
| boolean cancel( boolean mayInterruptIfRunning) |
试图取消该 Future 里关联的 Callable 任务。 |
| V get() |
返回 Callable 任务里 call() 方法的返回值。调用该方法将导致程序阻塞, 必须等到子线程结束后才会得到返回值。 |
| V get( long timeout, TimeUnit unit) |
返回 Callable 任务里 call() 方法的返回值。 该方法让程序最多阻塞 timeout 和 unit 指定的时间,如果经过指定时间后 Callable 任务依然没有返回值,将会抛出 TimeoutException 异常。 |
| boolean isCancelled() |
如果在 Callable 任务正常完成前被取消,则返回 true。 |
| boolean isDone() |
如果 Callable 任务已完成,则返回 true。 |
所以使用Callable来创建多线程的步骤如下
- 创建实现Callable接口的类并实现call()方法,该方法作为线程执行体。
- 使用FutureTask对象封装Callable对象的call()方法的返回值
- 使用FutureTask多为Thread对象的target创建并启动新线程
- 使用FutureTask的get()方法来获得返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import java.util.concurrent.Callable; import java.util.concurrent.FutureTask;
public class ThirdThread { public static void main(String[] args) { ThirdThread rt = new ThirdThread(); FutureTask<Integer> task = new FutureTask<>((Callable<Integer>)()->{ int i = 0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i); } return i; }); for(int i = 0;i<100;i++) { System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i); if(i==20) { new Thread(task,"有返回值的线程").start(); } } try { System.out.println("子线程的返回值:"+task.get()); } catch (Exception e) { e.printStackTrace(); } } }
|
三种创建方法的对比
- 由于Java是单继承的,所以一个类继承了Thread就无法再继承其他类了,不过书写比较简单,如果需要访问当前线程,直接使用this就可以获得。
- 线程只是实现了Runable接口或Callable接口,还可以继承其他类,在这种情况下,多个线程可以共享同一份资源。不过访问当前线程的话需要使用
Thread.cuuentThread()方法。
综上所述:尽量使用接口创建。
线程的声明周期

本篇在知乎中写的还是比较详细的了:多线程简单入门,我就丢几个demo吧
新建和就绪状态
使用start()方法来把线程改变为就绪状态,只能对处于新建状态的线程调用该方法,否则就会引发IllegalThreadStateException异常。而且不是调用Run方法,是调用start方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class InvokeRun extends Thread{ private int i; public void run() { for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String[] args) { for(int i = 0;i<100;i++) { System.out.println(Thread.currentThread().getName()+":"+i); if(i == 20) { new InvokeRun().run(); new InvokeRun().run(); } } } }
|
运行和阻塞状态

线程死亡
run或者call方法执行完成后,线程正常结束,或者线程抛出一个未捕获的异常,直接调用线程的stop()方法,但是不推荐。可能造成死锁。而且不能对已经死亡的线程重写调用start方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class StartDead extends Thread{ private int i; public void run() { for(;i<100;i++) { System.out.println(getName()+":"+i); } } public static void main(String[] args) { StartDead sd = new StartDead(); for(int i = 0;i<300;i++) { System.out.println(Thread.currentThread().getName()+":"+i); if(i == 20) { sd.start(); System.out.println(sd.isAlive()); } if(i>20&&!sd.isAlive()) { sd.start(); } } } }
|
控制线程
join方法
当 在某 个 程序 执行 流 中 调用 其他 线程 的 join() 方法 时,调用线程将被阻塞,直到被 join() 方法加入的 join 线程执行完为止。join方法有三种重载形式
- join():等待被执行完
- join(long millis):最长执行millis毫秒
- join(long millis,int nanos):最长等待mills毫秒加nanos毫微秒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class JoinThread extends Thread { public JoinThread(String name) { super(name); } public void run() { for(int i=0;i<100;i++) { System.out.println(getName()+":"+i); } } public static void main(String[] args) throws InterruptedException { new JoinThread("新线程").start(); for(int i = 0;i<100;i++) { if(i == 20) { JoinThread jt = new JoinThread("被join的线程"); jt.start(); jt.join(); } System.out.println(Thread.currentThread().getName()+":"+i); } } }
|
后台线程
JVM的垃圾回收线程就是典型的后台线程。如果所有的前台线程都死亡,则后台线程会自动死亡。必须在线程启动之前就设置为后台线程,否则会引发IllegalThreadStateException 异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class DaemonThread extends Thread { public void run() { for(int i=0;i<100;i++) { System.out.println(getName()+":"+i); } } public static void main(String[] args) throws InterruptedException { DaemonThread t = new DaemonThread(); t.setDaemon(true); t.start(); for(int i = 0;i<10;i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
|
线程睡眠
如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态。sleep方法有两种重载方法:
static void sleep(long millis)暂停millis毫秒
static void sleep(long millis,int nanos)暂停millis毫秒+nanos毫微秒
1 2 3 4 5 6 7
| public class SleepTest{ public static void main(String[] args) throws Exception{ for(int i =0;i<10;i++){ Thread.sleep(1000); } } }
|
改变线程优先级
Thread 类提供了 setPriority( int newPriority)、 getPriority() 方法来设置和返回指定线程的优先级,其中 setPriority() 方法的参数可以是一个整数,范围是 1 ~ 10 之间,也可以使用 Thread 类的如下三个静态常量。
- MAX_ PRIORITY: 其值是 10。
- MIN_ PRIORITY: 其值是 1。
- NORM_ PRIORITY: 其值是 5。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| public class Improtant { public static void main(String[] args) { Thread t1=new Thread(()->{ System.out.println("线程1"); }); t1.start(); t1.setPriority(1); t1.setName("线程1"); System.out.println("线程名称:" + t1.getName()); System.out.println("线程的状态:" + t1.getState()); System.out.println("判断线程是否被激活:" + t1.isAlive()); System.out.println("当前正在执行的线程:" + Thread.currentThread()); try { t1.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } Thread t2=new Thread(()->{ System.out.println("线程2"); }); t2.start(); t2.setPriority(10); Thread t3=new Thread(()->{ System.out.println("线程3"); }); t3.start(); t3.setPriority(5); try { t3.join(); } catch (InterruptedException e) { e.printStackTrace(); } Thread t4=new Thread(()->{ System.out.println("线程4"); }); t4.start(); t4.yield(); Thread t5=new Thread(()->{ System.out.println("线程5"); }); t5.start(); Thread t6=new Thread(()->{ System.out.println("线程6"); }); t6.start(); System.out.println(t1.getPriority()); System.out.println(t1.getId()); System.out.println(t2.getName()); t1.interrupt(); System.out.println(t1.interrupted()); System.out.println("线程的状态:" + t1.getState()); } }
|