Java-多线程
Java 多线程编程
一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径
多线程是多任务的一种特别形式,但多线程使用了更小的资源开销
进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,他必须是进程的一部分。一个进程一直运行,直到所有的非守候线程都结束运行后才能结束
-
一个线程的生命周期
新建状态--(执行start()
方法)-->就绪状态--(执行run()
方法)-->运行状态-->死亡状态
运行状态-->阻塞状态-->就绪状态- 新建状态:
使用new
关键字和Thread
类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start()
这个线程 - 就绪状态:
当线程对象调用了start()
方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度 - 运行状态:
如果就绪状态的线程获取CPU资源,就可以执行run()
,此时线程处于运行状态。处于运行状态的线程最为复杂,他可以变为阻塞状态、就绪状态和死亡状态 - 阻塞状态:
如果一个线程执行了sleep()
睡眠、suspend()
挂起等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行wait()
方法,使线程进入到等待阻塞状态
同步阻塞:线程在获取synchronized
同步锁失败(同步锁被其他线程占用)
其他阻塞:通过调用线程的sleep()
或join()
发出了I/O请求时,线程就会进入阻塞状态。当sleep()
状态超时,join()
等待线程终止或超时,或者I/O处理完毕,线程重新转入就绪状态 - 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态
- 新建状态:
-
线程的优先级
每一个Java线程都有一个优先级,这样有助于操作系统确定线程的调度顺序
Java线程的优先级是一个整数,取值范围是1(Thead.MIN_PRIORITY
)-10(Thead.MAX_PRIORITY
),默认情况下,每一个线程都会分配一个优先级NORM_PRIORITY(5)
-
创建一个线程
三种方法:实现Runnable
接口、继承Thread
类本身、Callable
和Future
创建线程 -
通过实现
Runnable
接口创建线程class RunnableDemo implements Runnable {
private Thread t;
private String threadName;RunnableDemo(String name) { threadName = name; System.out.println("Creating" + threadName); } // 为了实现Runnable,一个类只需要执行一个方法调用`run()` public void run() { System.out.println("Running" + threadName); try { for(int i = 4; i > 0; i--) { Thread.sleep(50); } }catch (InterruptedException e) { System.out.println("Thread" + threadName + "interrupted" ); } System.out.println("Thread" + threadName + "exiting"); } public void start () { System.out.println("Starting" + threadName); if (t == null) { t = new Thread (this, threadName); t.start (); } }
}
public class TestThread {
public start void main(String args[]) {
RunnableDemo R1 = new RunnableDemo("Thread-1");
R1.start();RunnableDemo R2 = new RunnableDemo("Thread-2"); R2.start(); }
}
-
通过继承
Thread
来创建线程
创建一个线程的第二种方法是创建一个新的类,该类继承Thread
类,然后创建一个该类的实例
继承类必须重写run()
方法,该方法是新线程的入口点。它也必须调用start()
方法才能执行class ThreadDemo extends Thread { private Thread t; private String threadName; ThreadDemo(String name) { threadName = name; System.out.println("Creating" + threadName); } public void run() { System.out.println("Running" + threadName); try { for(int i = 4; i > 0; i--) { System.out.println("Thread:" + threadName + "," + i); THread.sleep(50); } }catch (InterruptedException e) { System.out.println("Thread" + threadName + "interrupted"); } System.out.println("Thread" + threadName + "exiting"); } public void start () { System.out.println("Starting" + threadName); if (t==null) { t = new Thread (this, threadName); t.start(); } } } public class TestThread { public static void main(String args[]) { ThreadDemo T1 = new ThreadDemo("Thread-1"); T1.start(); ThreadDemo T2 = new ThreadDemo("Thread-2"); T2.start(); } }
-
通过
Callable
和Future
创建线程-
创建
Callable
接口的实现类,并实现call()
方法,该call()
方法将作为线程执行体,并且有返回值 -
创建
Callable
实现类的实例,使用FutureTask
类来包装Callable
对象,该FutureTask
对象封装了该Callable
对象的Call()
方法的返回值 -
使用
FutureTask
对象作为Thread
对象的target
创建并启动新线程 -
使用
FutureTask
对象的get()
方法来获得子线程执行结束后的返回值public class CallableThreadTest implements Callable<Integer> { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<>(ctt); for(int i = 0;i < 100;i++) { System.out.println(Thread.currentThread().getName()+ "的循环变量i的值"+i); if(i==20) { new Thread(ft,"有返回值的线程").start(); } } try{ System.out.println("子线程的返回值:" + ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionEXception e) { e.printStackTrace(); } } @Override public Integer call() throws Exception { int i = 0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } return i; } }
-
-
创建线程的三种方式的对比
- 采用实现
Runnable
、Callable
接口的方式创建多线程是,线程类只是实现了Runnable
接口或Callable
接口,还可以继承其他类 - 使用继承
Thread
类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()
方法,直接使用this
即可获得当前线程
- 采用实现
-
线程的几个主要概念
线程同步、线程间通信、线程死锁、线程控制:挂起-停止-恢复