JAVA—多线程
一、线程简介
- 程序:指令和数据的有序集合,无任何运行的含义,是一个静态的概念。
- 进程:执行程序的一次执行过程,是系统资源分配的单位。
- 线程:CPU调度和执行的单位,一个进程中可包含若干个,且至少一个线程。
线程概念总结: - 线程是独立的执行路径,。
- 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程。
- ** main()称为主线程,为系统的入口,用于执行整个程序。**
- 在一个进程中,如果开辟多个线程,线程的运行由调度器安排调度,调度器与操作系统紧密相关,先后顺序人为无法干预。
- 在对于同一份资源操作时,线程之间会存在资源抢夺问题,需要加入并发控制。
- 线程会带来额外的开销。
- 每个线程在自己工作内存交互,内存控制不到会造成数据不一致。
二、线程实现
1、Thread class(继承Thread类)
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用**start()**方法启动线程
Thread对象常用方法:
- 1 public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 - 2 public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 - 3 public final void setName(String name)
改变线程名称,使之与参数 name 相同。 - 4 public final void setPriority(int priority)
更改线程的优先级。 - 5 public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。 - 6 public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。 - 7 public void interrupt()
中断线程。 - 8 public final boolean isAlive()
测试线程是否处于活动状态。
Thread类常用静态方法:
- 1 public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。 - 2 public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 - 3 public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 - 4 public static Thread currentThread()
返回对当前正在执行的线程对象的引用。 - 5 public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。
//代码详解
public class MyThread {
//main(),主线程
public static void main(String[] args) {
//创建自定义线程对象; 并调用start(),开启线程
MyThread_01 myThread01 = new MyThread_01();
myThread01.start();
int i = 100;
while (i >= 0){
System.out.println("主线程"+i);
i--;
}
}
}
//自定义线程类继承Thread类
class MyThread_01 extends Thread{
//重写run()方法,编写线程执行体
public void run(){
int i = 0;
while (i <= 100){
System.out.println("子线程"+i);
i++;
}
}
}
2、Runnable接口(实现Runnable接口)
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建MyRunnable对象,并通过代理MyRunnable对象创建Thread对象,Thread对象调用start()方法启动线程
Thread类代理MyRunnable对象常用构造器:
Thread(Runnable threadOb,String threadName);
//代码详解
public class MyThread {
//main(),主线程
public static void main(String[] args) {
//创建自定义线程对象
MyThread_02 myThread02 = new MyThread_02();
//创建代理类对象
Thread myThread = new Thread(myThread02);
//代理类对象调用start()启动线程
myThread.start();
int i = 100;
while (i >= 0){
System.out.println("主线程"+i);
i--;
}
}
}
//自定义线程类实现Runnable接口
class MyThread_02 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
int i = 0;
while (i <= 100){
System.out.println("子线程"+i);
i++;
}
}
}
public class MyThread {
//main(),主线程
public static void main(String[] args) {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
//创建代理类对象,且同一个对象被多个线程使用
Thread myThread_01 = new Thread(myThread01,"线程A");
Thread myThread_02 = new Thread(myThread01,"线程B");
Thread myThread_03 = new Thread(myThread01,"线程C");
//代理类对象调用start()启动线程
myThread_01.start();
myThread_02.start();
myThread_03.start();
int i = 100;
while (i >= 0){
System.out.println("主线程"+i);
i--;
}
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
int i = 0;
while (i <= 100){
System.out.println("子线程"+i);
i++;
}
}
}
注意:Thread和Runnable小结
(1)继承Thread()类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
(2)实现Runnable接口
- 实现Runnable接口具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
3、Callable接口(实现Callable接口)
- (1)实现Callable接口,需要返回值
- (2)重写Call方法,需要抛出异常
- (3)创建目标对象
- (4)创建执行服务:ExecutorService myService = Executors.newFixedThreadPoll(n); n为开启线程数量
- (5)提交执行:Future result = ser.submit(t1);
- (6)获取结果:boolean rt = result.get()
- (7)关闭服务:ser.shutdownNow();
//代码详解
public class MyCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建目标对象
TestCallable myCallable_01 = new TestCallable();
TestCallable myCallable_02 = new TestCallable();
TestCallable myCallable_03 = new TestCallable();
//创建执行服务
ExecutorService myService = Executors.newFixedThreadPool(3);
//向服务内提交执行
Future<Boolean> r1 = myService.submit(myCallable_01);
Future<Boolean> r2 = myService.submit(myCallable_02);
Future<Boolean> r3 = myService.submit(myCallable_03);
//获取结果
boolean a = r1.get();
boolean b = r2.get();
boolean c = r3.get();
//关闭服务
myService.shutdown();
}
}
class TestCallable implements Callable{
@Override
public Boolean call() throws Exception {
int i = 0;
while (i <= 10){
System.out.println("子线程"+i);
i++;
}
return true;
}
}
三、线程状态
- 新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。 - 就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。 - 运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。 - 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。 - 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
1、线程停止
- 自定义线程类中设置标识位,并设置改变标识位的方法
- main()中设置线程停止条件,并且自定义线程类对象调用改变标识为的方法
public class MyThread {
//main(),主线程
public static void main(String[] args) {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
//创建代理类对象
Thread myThread = new Thread(myThread01,"线程A");
//代理类对象调用start()启动线程
myThread.start();
int i = 0;
while (i <= 1000){
System.out.println("主线程"+i);
i++;
//主线程中设置子线程标识为改变条件
if(i == 500){
myThread01.stop();
}
}
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//定义线程停止标识位
Boolean flag = true;
//重写run()方法,编写线程执行体
public void run(){
int i = 0;
while (flag){
System.out.println("子线程"+i);
i++;
}
}
public void stop(){
this.flag = false;
}
}
2、线程休眠
- Thread.sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在interruptedException
- sleep时间达到后进入就绪状态
- 每个线程对象都有一个锁,sleep不会释放锁
public class MyThread {
//main(),主线程
public static void main(String[] args) {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
//创建代理类对象
Thread myThread = new Thread(myThread01,"线程A");
//代理类对象调用start()启动线程
myThread.start();
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
int i = 0;
while (i <= 20){
System.out.println("子线程"+i);
i++;
try {
//线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3、线程礼让
- 礼让(Thread.yield())不一定成功
- Thread.yield()编写在run()方法内
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
MyThread_01 myThread02 = new MyThread_01();
//创建代理类对象
Thread Thread01 = new Thread(myThread01,"线程A");
Thread Thread02 = new Thread(myThread02,"线程B");
//代理类对象调用start()启动线程
Thread01.start();
Thread02.start();
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束执行");
}
}
4、线程强制执行
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
MyThread_01 myThread02 = new MyThread_01();
//创建代理类对象
Thread Thread01 = new Thread(myThread01,"线程A");
Thread Thread02 = new Thread(myThread02,"线程B");
//代理类对象调用start()启动线程
Thread01.start();
for (int i = 0; i <= 1000; i++){
System.out.println("主线程"+i);
if (i == 200){
//在主方法下增加join()方法,只影响主线程,不影响其他子线程
Thread02.start();
Thread02.join();
}
}
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
int i = 0;
while (i <= 200){
System.out.println(Thread.currentThread().getName()+i);
i++;
}
}
}
5、观察线程状态
- NEW:线程尚未启动
- RUNNABLE:线程正在执行
- BLOCKED:线程被阻塞,等待监视器锁定
- WAITING:该线程正在等待另外一个线程执行特定动作
- TIMED_WAITING:该线程正在等待另外一个线程执行动作到达指定时间
- TERMINATED:线程已退出
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
//创建代理类对象
Thread Thread01 = new Thread(myThread01,"线程A");
//代理类对象调用start()启动线程
System.out.println("当前线程状态"+Thread01.getState()); //NEW:Thread01尚未启动
Thread01.start();
System.out.println("当前线程状态"+Thread01.getState()); //RUNNNABLE:Thread01正在执行
Thread.sleep(1200);
System.out.println("当前线程状态"+Thread01.getState()); //TIMEF_WAITING:Thread01等待主线程执行动作到达指定时间
Thread.sleep(11000);
System.out.println("当前线程状态"+Thread01.getState()); //TERMINATED:Thread01已退出
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
int i = 0;
while (i <= 10){
System.out.println("--------------"+Thread.currentThread().getName()+"正在执行-----------------");
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
6、线程优先级
- 线程对象使用setPriority(优先级)设置各线程优先级
- 优先级高的线程不一定先执行
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
MyThread_01 myThread02 = new MyThread_01();
//创建代理类对象
Thread Thread01 = new Thread(myThread01,"线程A");
Thread Thread02 = new Thread(myThread02,"线程B");
//代理类对象调用start()启动线程
Thread01.start();
//设置线程Thread02优先级,并启动Thread02
Thread02.setPriority(Thread.MAX_PRIORITY);
Thread02.start();
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
System.out.println(Thread.currentThread().getName()+"正在执行——>"+Thread.currentThread().getPriority());
}
}
7、守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不应等待守护线程执行完毕
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建自定义线程对象
MyThread_01 myThread01 = new MyThread_01();
MyThread_01 myThread02 = new MyThread_01();
//创建代理类对象
Thread Thread01 = new Thread(myThread01,"线程A");
Thread Thread02 = new Thread(myThread02,"线程B");
//代理类对象调用start()启动线程
Thread01.start();
//设置线程Thread02为守护线程,并启动Thread02
Thread02.setDaemon(true); //用户线程为false,守护线程为ture
Thread02.start();
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
//重写run()方法,编写线程执行体
public void run(){
int i;
for(i = 0; i <= 500; i++){
System.out.println("---------------"+Thread.currentThread().getName()+"正在执行"+i+"--------------------");
}
}
}
四、线程同步
- synchronized需要锁主共享资源,即run()方法中调用的资源
- synchronized默认锁本身即this
1、同步方法
- synchronized 修饰方法原理:
public class SynchronizedDemo {
public synchronized void method() {
System.out.println("synchronized 方法");
}
}
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建自定义线程对象,使用synchronized时只有一个自定义线程对象,对同一个自定义线程对象操作
MyThread_01 myThread01 = new MyThread_01();
//创建代理类对象
Thread Thread01 = new Thread(myThread01,"线程A");
Thread Thread02 = new Thread(myThread01,"线程B");
//代理类对象调用start()启动线程
Thread01.start();
Thread02.start();
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
private int i = 10;
private Boolean flag = true;
//重写run()方法,编写线程执行体
public void run(){
while (flag){
try {
subtraction();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//锁住共享资源,即run()中调用的资源
private synchronized void subtraction() throws InterruptedException {
if(i == 0){
this.flag = false;
return;
}
Thread.sleep(100);
System.out.println("------"+Thread.currentThread().getName()+"正在执行:"+i+"-------");
i--;
}
}
2、同步块
- 同步块原理:
synchronized (obj){}
- obj称之为同步监视器
- obj可以是任何对象,但一般使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,以为同步方法的同步监视器就是this,就是这个对象本身,或称为class
注意:
- 使用同步块时synchronized后必须跟对象,不能时基本类型
- 在run()内使用同步块时必须完全等待某个线程执行完一次run()方法,另一个进程才能执行
public class Singleton {
//保证有序性,防止指令重排
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
//先判断对象是否已经实例过,没有实例化过才进入加锁代码
if (uniqueInstance == null) {
//类对象加锁
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
3、Lock(锁)
- Lock原理:
class A{
private final ReentrantLock lock = new ReentrantLock();
public void m(){
lock.lock();
try {
//保证代码安全性
}finally {
lock.unlock();
//如果代码有异常,将unlock()写入finally语句块
}
}
}
//代码详解
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建自定义线程对象,使用synchronized时只有一个自定义线程对象,对同一个自定义线程对象操作
MyThread_01 myThread01 = new MyThread_01();
MyThread_01 myThread02 = new MyThread_01();
//创建代理类对象
Thread Thread01 = new Thread(myThread01,"线程A");
Thread Thread02 = new Thread(myThread01,"线程B");
//代理类对象调用start()启动线程
Thread01.start();
Thread02.start();
}
}
//自定义线程类实现Runnable接口
class MyThread_01<i> implements Runnable{
private final ReentrantLock lock = new ReentrantLock();
private Integer i = 10;
private Boolean flag = true;
//重写run()方法,编写线程执行体
public void run() {
while (flag){
m();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void m(){
lock.lock();
try {
if(i>=0){
System.out.println("------"+Thread.currentThread().getName()+"正在执行:"+i+"-------");
i--;
}else {
this.flag = false;
return;
}
}finally {
lock.unlock();
}
}
}
4、死锁
(1)死锁定义
- 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
(2)解除办法
- 死锁预防。
这是一种较简单和直观的事先预防的方法。方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被广泛使用。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。 - 死锁避免。
系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源;如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。 - 死锁检测和解除。
先检测:这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源。检测方法包括定时检测、效率低时检测、进程等待时检测等。
然后解除死锁:采取适当措施,从系统中将已发生的死锁清除掉。
这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
(3)排除方法
- 1、撤消陷于死锁的全部进程;
- 2、逐个撤消陷于死锁的进程,直到死锁不存在;
- 3、从陷于死锁的进程中逐个强迫放弃所占用的资源,直至死锁消失。
- 4、从另外一些进程那里强行剥夺足够数量的资源分配给死锁进程,以解除死锁状态
五、线程池
1、线程池定义
- 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
2、组成部分
服务器程序利用线程技术响应客户请求已经司空见惯,可能您认为这样做效率已经很高,但您有没有想过优化一下使用线程的方法。该文章将向您介绍服务器程序如何利用线程池来优化性能并提供一个简单的线程池实现。
- 1、线程池管理器(ThreadPoolManager):用于创建并管理线程池
- 2、工作线程(WorkThread): 线程池中线程
- 3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
- 4、任务队列:用于存放没有处理的任务。提供一种缓冲机制。
3、代码详解
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread {
//main(),主线程
public static void main(String[] args) throws InterruptedException {
//创建服务,创建线程池
ExecutorService service =Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread_01());
service.execute(new MyThread_01());
service.execute(new MyThread_01());
//关闭链接
service.shutdown();
}
}
//自定义线程类实现Runnable接口
class MyThread_01 implements Runnable{
private final ReentrantLock lock = new ReentrantLock();
private Integer i = 10;
private Boolean flag = true;
//重写run()方法,编写线程执行体
public void run() {
while (flag){
m();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void m(){
if(i>=0){
System.out.println("------"+Thread.currentThread().getName()+"正在执行:"+i+"-------");
i--;
}else {
this.flag = false;
return;
}
}
}