Java-thread多线程

进程与线程

进程是操作系统资源分配的基本单位,每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;在操作系统中能同时运行多个进程(程序);系统在运行的时候会为每个进程分配不同的内存空间;没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。

线程是任务调度和执行的基本单位,线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行);对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。

线程相关基本概念

多进程:是指操作系统能同时运行多个任务(程序)。

多线程:是指在同一程序中有多个顺序流在执行。

主线程:当Java程序启动时,一个线程立刻运行,该线程通常叫做程序的主线程,使用java命令就启动一个虚拟机进程,java虚拟机会创建一个主线程,该程序入口main()方法执行

主线程特点:

  1. 是产生其他子线程的线程
  2. 不一定是最后执行的线程,子线程可能在主线程结束后执行

子线程:在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同,子线程执行顺序不受程序员控制、JVM自行调动

普通线程(用户线程):只完成用户自己想要完成的任务,不提供公共服务

守护线程(后台线程):只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

线程组:在Java中,线程组是指java.lang.ThreadGroup类的对象,每个线程都隶属于唯一的一个线程组,如果没有定义会默认一个线程组,也可以指定线程属于某个线程组,每一个应用都至少有一个线程属于系统线程组,这个线程组在线程创建时指定并在线程的整个生命周期内都不能更改。可以通过调用包含ThreadGroup类型参数的Thread类构造方法来指定线程所属线程组。若没有指定,则线程默认的隶属于名为main的系统线程组。除了预建的系统线程外,所以线程组都必须显式创建。

线程的生命周期、状态(重点)

在这里插入图片描述
新建状态(New):新创建了一个线程对象。使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法,该线程就进入就绪状态。该状态的线程位于可运行线程池中,就绪状态的线程处于就绪队列中,变得可运行,等待获取CPU的使用权,要等待JVM里线程调度器的调度。

运行状态(Running):如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程的创建

创建线程的方法有两种:

  1. 继承Thread类,重写该类的run()方法
  2. 实现Runnable接口,重写该接口的run()方法

线程的启动:调用线程对象的start()方法。线程运行的是创建线程类中重写的run()方法。

public class CreateThread {

    // 继承Thread类本身
    static class extsThread extends Thread {

        @Override
        public void run() {

        }
    }

    // 实现Runnable接口
    static class implRunnable implements Runnable {

        @Override
        public void run() {

        }
    }

    public static void main(String[] args) {
        // 方法一:继承Thread类,创建线程对象
        Thread thread1 = new extsThread();
        // 启动线程
        thread1.start();

        // 方法二:实现Runnable接口,创建实现类对象
        Runnable runnable = new implRunnable();
        // 再将实现类对象作为参数,传递打炮Thread创建线程对象
        Thread thread2 = new Thread(runnable);
        // 启动线程
        thread2.start();
    }
}

线程的优先级

每一个Java线程都有一个优先级,Java线程的优先级用整数表示,线程的优先级别不会决定哪个线程先启动,只能建议JVM哪个线程先执行,高优先级的线程比低优先级的线程有更高的几率得到执行,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间(分别为1和10)

默认情况下,新创建的线程都拥有和创建它的线程相同的优先级。main方法所关联的初始化线程拥有一个默认的优先级,这个优先级是Thread.NORM_PRIORITY (5),线程的当前优先级可以通过getPriority方法获得,线程的优先级可以通过setPriority方法来动态的修改,一个线程的最高优先级由其所在的线程组限定

Thread类有以下三个静态常量:

    public final static int MIN_PRIORITY = 1;

    public final static int NORM_PRIORITY = 5;

    public final static int MAX_PRIORITY = 10;

Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级

    /**
     * thread是一个线程对象
     */
 	 
    // 设置线程优先级
    thread.setPriority(Thread.MIN_PRIORITY);
    thread.setPriority(8);
    // 获得线程优先级
    thread.getPriority();

线程的调度

1、线程睡眠(sleep):Thread.sleep(long millis)方法,使线程转到阻塞状态,等待线程睡眠设定时间后进入就绪状态,不释放锁。millis参数设定睡眠的时间,以毫秒为单位sleep()平台移植性好。

    try {
        // 当前线程睡眠1秒
        Thread.sleep(1000);   
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

2、线程让步(yield):Thread.yield() 方法,暂停当前正在执行的线程对象,使线程转到就绪状态,不释放锁,把执行机会让给相同或者更高优先级的线程。

public class ThreadYield extends Thread{
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			System.out.println("Thread Name : " + Thread.currentThread().getName()  + " " + i);
		}
	}
	
	public static void main(String[] args) {
		
		// 交互执行
		ThreadYield threadYield1 = new ThreadYield();
		ThreadYield threadYield2 = new ThreadYield();
		
		Thread thread = new Thread("Thread Yield") {
			@Override
			public void run() {
				for (int i = 1; i <= 10; i++) {
					System.out.println("Thread Name : " + Thread.currentThread().getName() + " " + i);
					if (i == 4) {
						// 当前线程让步
						this.yield();
					}
				}
			}
		};
		thread.start();
		threadYield1.start();
		threadYield2.start();
	}
}

3、线程加入(join):Thread.join()方法,在当前线程中调用另一个线程的join()方法,则使当前线程转入阻塞状态,直到另一个线程运行结束,当前线程再由阻塞转为就绪状态。join()方法底层用wait()实现,所以会使当前线程释放锁。

public class ThreadJoin extends Thread {
   
   private Thread thread;
   
   public ThreadJoin() {
   	
   }
   
   public ThreadJoin(Thread thread) {
   	this.thread = thread;
   }
   
   @Override
   public void run() {
   	
   	for (int i = 1; i < 10; i++) {
   		if (i == 4) {
   			
   			thread.start();
   			
   			try {
   				// 加入一个线程myThread,等myThread,执行完threadJoin2才继续执行
   				thread.join();
   				
   			} catch (InterruptedException e) {
   				// TODO Auto-generated catch block
   				e.printStackTrace();
   			}
   		}
   		System.out.println(this.currentThread().getName() + " : " + i);
   	}
   	
   }
   
   public static void main(String[] args) {
   	MyThread myThread = new MyThread();
   	ThreadJoin threadJoin2 = new ThreadJoin(myThread);
   	threadJoin2.start();
   }
}

4、线程等待(wait):Object.wait()方法,导致当前的线程等待,进入阻塞状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法,使该线程进入就绪状态,释放锁。

5、线程唤醒(notify):Object.notify()方法和Object.notifyAll()方法,唤醒在此对象监视器上等待的单个线程或所有线程,使线程从阻塞状态变成就绪状态。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。

public class ThreadWait {
	public static void main(String[] args) {
	
		final Test t = new Test();
		
		Thread t1 = new Thread("A") {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						synchronized (t) {
							if(i!=0) 
								// 线程的唤醒
								t.notify();
							System.out.println("A");
							t.wait();
						}
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		
		Thread t2 = new Thread("B") {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						synchronized (t) {
							t.notify();
							System.out.println("B");
							if(i!=9) 
								// 线程等待
								t.wait();
						}
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		
		t1.start();
		
		try {
			Thread.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		t2.start();
	}
}

注意:Thread中suspend()和resume()两个方法在JDK1.5中已经废除,不再介绍。因为有死锁倾向。

线程的同步

线程同步:保证多个线程同时读取一个类中的共享数据的线程安全

Java所有对象都有一个内置锁,使用 synchronized 关键字修饰方法或代码块时将为当前对象加锁,一个线程获取锁后,其他线程需要等待该线程执行完毕后解锁。

有两种同步的方法:

  1. synchronized修饰方法
  2. synchronized修饰代码块
	// 修饰方法
	private synchronized void function() {
	
	}

	// 修饰代码块
	private void function() {
 		synchronized (object) {
  	  
 	    }
	}

参考:
https://www.cnblogs.com/Qian123/p/5670304.html
https://www.cnblogs.com/yjd_hycf_space/p/7526608.html
http://www.runoob.com/java/java-multithreading.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值