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()); } }