并发编程-Java线程
一、概念
-
并发:指一个处理器在同一个时间间隔内处理多个任务,是逻辑上的同时发生。
-
并行:指多个处理器或是多核的处理器在同一时间间隔处理多个不同的任务,是物理上的同时发生。
-
线程:线程是程序执行流的最小单元,是系统独立调度与分配CPU(独立运行)的基本单位。
-
进程:进程是资源分配的基本单位,一个进程可以包含一个或多个线程。
二、线程的状态
计算机的线程可分为5个状态:
-
NEW(新建): 新创建的线程对象,该线程状态会保持到调用start()方法前。
-
RUNNABLE(可运行/就绪): 线程对象新建后,其他线程(比如main线程)调用了改对象的start()方法,该线程的状态就进入可运行线程池中,等待被线程池调度选中,最后获得CPU的使用权。
-
RUNNING(运行): 可运行状态的线程获得CPU的时间片(timeslice),执行程序代码。
-
BLOCKED(阻塞) 阻塞状态是指线程因为某种原因放弃了CPU的使用权,也即让出了CPU 时间片(timeslice),暂时停止运行,直到线程重新进入可运行状态,才有机会再次获得CPU的使用权以及获得分配的时间片,最后再次进入运行状态。
-
DEAD(死亡): 线程run()、main()方法执行结束,或者因异常退出了run()方法,则改线程结束生命周期;死亡的线程不可再次复生。
而Java中为了更好地管理线程,将线程的状态进行了调整,将RUNNABLE
与RUNNING
作为一个RUNNABLE
状态,而对于BLOCKED
状态,Java根据阻塞的原因以及是否会释放锁来细分它阻塞的类型。
可以看到源码中的线程枚举类Thread.State
:
public enum State {
/**
* 新建且尚未启动的线程的状态.
*/
NEW,
/**
* 可运行线程的线程状态。处于可运行状态的线程正在Java虚拟机中执行,但它可能正在等待来自操作系统的其他资源,比如处理器。
* 也就是将就绪状态与运行状态的线程作为一种运行状态
*/
RUNNABLE,
/**
* 等待监视器锁的阻塞线程的线程状态。处于阻塞状态的线程正在等待监视器锁进入一个同步的块/方法,或者在调用Object.wait()后重新进入一个同步的块/方法。
*/
BLOCKED,
/**
*等待线程的线程状态。线程由于调用以下方法之一而处于等待状态:
* Object.wait();
* Thread.join();
* LockSupport.park();
*处于等待状态的线程正在等待另一个线程执行特定的操作:
*调用Object.notify()或者Object.notifyAll();
*调用 thread .join()的线程正在等待指定的线程结束;
*/
WAITING,
/**
*具有指定等待时间的等待线程的状态。由于调用了以下方法中的一个,并且指定了一个正的等待时间,线程处于计时等待状态:
*Thread.sleep(long);
*Object.wait(long);
*Thread.join(long);
*LockSupport.parkNanos();
*LockSupport.parkUntil();
*/
TIMED_WAITING,
/**
* 终止线程的线程状态。线程已完成执行。
*/
TERMINATED;
}
三、Java线程的创建
Java中可以通过三种去创建线程,继承Thread类、实现Runnable接口、实现Callable接口;需要注意的是Java只是作为一个高级语言去调用底层代码来创建线程,而不是直接开启一个线程。
3.1、继承Thread类
- 自定义一个线程类继承Thread类
- 重写run()方法,并在里面编写线程执行体
- 实例化一个自定义的线程对象,调用start()方法启动线程
//自定义线程类
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("我是一个子线程");
}
}
public class Test{
public static void main(String[]args){
System.out.println("我是主线程");
MyThread myThread = new MyThread(); //实例一个子线程
myThread.start(); //启动子线程
}
3.2、实现Runnable接口
- 自定义一个线程类,并实现Runnable接口
- 重写run()方法,并在里面编写线程执行体
- 实例化一个自定义线程的对象
- 实例化一个代理线程对象,并把自定义线程对象放进去
- 调用代理对象的start()方法启动线程
public class MyRunnable implements Runnable{
@Override
public void run() {
//子线程任务
}
}
public class Test{
public static void main(String[]args){
System.out.println("我是主线程");
MyRunnable myRunnable = new MyRunnable(); //实例化一个子线程
Thread thread = new Thread(myRunnable); //实例化一个代理线程对象
thread.start(); //启动子线程
}
}
3.3、实现Callable接口
- 自定义一个线程类,并实现Callable接口
- 重写call()方法,编写线程执行体,并处理抛出异常
- 实例化一个目标对象
- 实例化一个执行服务对象
- 提交执行
- 获取子线程执行返回的结果
- 关闭服务
publi class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("我是一个子线程");
return "执行成功";
}
}
public class Test{
public static void main(String[]args){
System.out.println("我是主线程");
MyCallable myCallable = new myCallable(); //实例一个子线程
ExecutorService service = Executors.newFixedThreadPool(2); //创建一个线程服务池
Future<String> future = service.submit(myCallable); //提交服务执行
try {
String result = future.get(); //获取服务返回结果
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}finally {
service.shutdown(); //关闭服务线程池
}
}
}
四、线程的优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度按照线程优先级决定应该调度哪个线程来执行
-
线程的优先级分为1-10;
- Thread.MIN_PRIORITY=1
- Thread.MAX_PRIORITY=10
- Thread.NORM_PRIORITY=5
-
优先值越高、数字越大;
-
默认优先值为5;
通过使用getPriority()以及setPriority(int xx)可以获取以及改变线程的优先级。
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"的优先级:-->"+Thread.currentThread().getPriority());
}
}
public static void main(String[] args) {
System.out.println("主线程默认的优先级:"+Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority,"A");
Thread t2 = new Thread(myPriority,"B");
Thread t3 = new Thread(myPriority,"C");
Thread t4 = new Thread(myPriority,"D");
Thread t5 = new Thread(myPriority,"E");
Thread t6 = new Thread(myPriority,"F");
//设置修改线程优先级
t2.setPriority(2);
t3.setPriority(7);
t4.setPriority(Thread.MAX_PRIORITY);
t5.setPriority(Thread.MIN_PRIORITY);
t6.setPriority(Thread.NORM_PRIORITY);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
优先级低只意味着获得调度的概率低,并不是优先级低就会在最后执行,主要还看CPU的调度策略。
五、守护线程
守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
将非守护线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出一个IllegalThreadStateException异常。不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
(4)JVM必须等待用户线程执行完毕,而不需等待守护线程执行完毕。