一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
使用多线程可以编写高效率的程序来达到充分利用 CPU,可以大大提高系统整体的并发能力以及性能.
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。
java 提供了三种创建线程的方法:实现 Runnable 接口;继承 Thread 类本身;通过 Callable 和 Future 创建线程。
创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类,同时重写 run()方法.
然后创建Runnable实现类的实例,并以此实例作为Thread的target对象,即该Thread对象才是真正的线程对象。
新线程创建之后,你调用它的 start() 方法它才会运行。
package com.example.test; /** * @author ydx */ public class RunnableDemo implements Runnable{ /** * 线程名称 */ private String threadName; /** * 构造方法 * @param threadName 线程名称 */ public RunnableDemo(String threadName) { this.threadName = threadName; } @Override public void run() { System.out.println(threadName + " is running"); //业务 for (int i = 0; i < 5; i++) { System.out.println(threadName + " 执行 " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(threadName + " is exiting"); } public static void main(String[] args) { RunnableDemo runnable1 = new RunnableDemo("thread-1"); Thread thread1 = new Thread(runnable1); thread1.start(); RunnableDemo runnable2 = new RunnableDemo("thread-2"); Thread thread2 = new Thread(runnable2); thread2.start(); } }
第一次运行结果如下:
thread-1 is running thread-1 执行 0 thread-2 is running thread-2 执行 0 thread-2 执行 1 thread-1 执行 1 thread-2 执行 2 thread-1 执行 2 thread-1 执行 3 thread-2 执行 3 thread-1 执行 4 thread-2 执行 4 thread-2 is exiting thread-1 is exiting
第二次运行结果如下:
thread-1 is running thread-1 执行 0 thread-2 is running thread-2 执行 0 thread-1 执行 1 thread-2 执行 1 thread-1 执行 2 thread-2 执行 2 thread-1 执行 3 thread-2 执行 3 thread-2 执行 4 thread-1 执行 4 thread-1 is exiting thread-2 is exiting
可以看出两次运行结果是不一样的,每次两个线程的执行顺序是随机的.
创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。
继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
package com.example.test; /** * @author ydx */ public class ThreadDemo extends Thread { /** * 线程名称 */ private String threadName; public ThreadDemo(String threadName) { this.threadName = threadName; } @Override public synchronized void start() { System.out.println(threadName+ " is starting......"); super.start(); } @Override public void run() { System.out.println(threadName + " is running"); //业务 for (int i = 0; i < 3; i++) { System.out.println(threadName + " 执行 " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(threadName + " is exiting"); } public static void main(String[] args) { ThreadDemo thread1 = new ThreadDemo("thread-1"); thread1.start(); ThreadDemo thread2 = new ThreadDemo("thread-2"); thread2.start(); } }
运行结果如下:
thread-1 is starting...... thread-2 is starting...... thread-1 is running thread-1 执行 0 thread-2 is running thread-2 执行 0 thread-1 执行 1 thread-2 执行 1 thread-2 执行 2 thread-1 执行 2 thread-2 is exiting thread-1 is exiting
调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
package com.example.test; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @author ydx */ public class CallableTest implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 5; i++) { sum += i; System.out.println(i); //sleep 200ms Thread.sleep(200); } return sum; } public static void main(String[] args) { long start = System.currentTimeMillis(); CallableTest callableTest = new CallableTest(); FutureTask<Integer> futureTask = new FutureTask<>(callableTest); new Thread(futureTask, "thread-1").start(); CallableTest callableTest2 = new CallableTest(); FutureTask<Integer> futureTask2 = new FutureTask<>(callableTest2); new Thread(futureTask2, "thread-2").start(); try { System.out.println("thread-1的结果: " + futureTask.get()); System.out.println("thread-2的结果: " + futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("耗时: " + (end - start) + "ms"); } }
运行结果:
0 0 1 1 2 2 3 3 4 4 thread-1的结果: 10 thread-2的结果: 10 耗时: 1004ms
我们创建了两个线程, 每个线程计算0~4的和,单个线程耗时200ms * 5 = 1000ms,而最终两个线程的总耗时约1000ms,由此可见两个线程是并发进行.
Java提供了一些方法用于线程状态的控制。具体如下:
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法。
Thread.sleep(long millis)方法,millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。
Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法.
Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。
yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。
join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN PRIORITY ) - 10 (Thread.MAX PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。使用线程池的好处:
Java通过Executors提供四种线程池,分别为: