Java多线程

学习内容:

  1. 线程,进程,多线程
  2. 继承Thread类
  3. 实现Runnable类
  4. 实现Callable类
  5. 初识并发问题
  6. 线程的命名及获取
  7. 线程休眠
  8. 线程中止
  9. 线程礼让
  10. 线程强制执行
  11. 线程优先级
  12. 线程同步
  13. 程序死锁
  14. 守护线程
  15. 线程池

学习笔记:

1.线程,进程,多线程

什么是线程,什么是进程?
进程:一个应用程序。每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(进程是资源分配的最小单位)
线程:一个进程的执行场景/执行单元。同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

注意:一个进程可以启动多个线程,但是例如进程A和进程B的内存独立不共享

线程A和线程B是什么关系?
线程A和线程B,堆内存方法区 内存共享。但是* 栈内存 *独立,一个线程一个栈。
多线程的目的是为了提高程序的处理效率

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

2.继承Thread类

在java中要想实现多线程,有三种手段,一种是继续Thread类,一种是实现Runable接口,以及Callable接口。
这里先说Thread类实现多线程:
通过继承Thread类,并重写父类的run()方法实现:
代码:

	public class Threadtest extends Thread{

		private String name;
		public Threadtest(String name) {
			this.name = name;
		}
		public void run() {
			for(int i = 0 ; i < 50 ; i++) {
				System.out.println(this.name + "正在工作中……" + i);
			}
		}
	
		
	public static void main(String[] args) {

		Threadtest mt1 = new Threadtest("线程一");
		Threadtest mt2 = new Threadtest("线程二");
		Threadtest mt3 = new Threadtest("线程三");
		// 启动实例线程对象
		mt1.start();
		mt2.start();
		mt3.start();
	}
	
	}

输出结果:

线程一正在工作中……0
线程一正在工作中……1
线程二正在工作中……0
线程二正在工作中……1
线程三正在工作中……0
线程二正在工作中……2
线程一正在工作中……2
线程二正在工作中……3
线程三正在工作中……1
线程二正在工作中……4
线程一正在工作中……3
线程二正在工作中……5
线程三正在工作中……2
线程二正在工作中……6
线程一正在工作中……4
线程二正在工作中……7
线程三正在工作中……3
线程二正在工作中……8
线程一正在工作中……5
线程二正在工作中……9
线程三正在工作中……4
线程二正在工作中……10
线程一正在工作中……6
线程二正在工作中……11
线程三正在工作中……5
线程二正在工作中……12
线程一正在工作中……7
线程二正在工作中……13
线程三正在工作中……6
线程二正在工作中……14
线程一正在工作中……8
线程二正在工作中……15
线程三正在工作中……7
线程二正在工作中……16
线程一正在工作中……9
线程二正在工作中……17
线程三正在工作中……8
线程二正在工作中……18
线程一正在工作中……10
线程二正在工作中……19
线程三正在工作中……9
线程二正在工作中……20
线程一正在工作中……11
线程二正在工作中……21
线程三正在工作中……10
线程二正在工作中……22
线程一正在工作中……12
线程二正在工作中……23
线程三正在工作中……11
线程二正在工作中……24
线程一正在工作中……13
线程二正在工作中……25
线程三正在工作中……12
线程二正在工作中……26
线程一正在工作中……14
线程二正在工作中……27
线程三正在工作中……13
线程二正在工作中……28
线程一正在工作中……15
线程二正在工作中……29
线程三正在工作中……14
线程二正在工作中……30
线程一正在工作中……16
线程二正在工作中……31
线程三正在工作中……15
线程二正在工作中……32
线程一正在工作中……17
线程二正在工作中……33
线程三正在工作中……16
线程二正在工作中……34
线程一正在工作中……18
线程二正在工作中……35
线程三正在工作中……17
线程二正在工作中……36
线程一正在工作中……19
线程二正在工作中……37
线程三正在工作中……18
线程二正在工作中……38
线程一正在工作中……20
线程二正在工作中……39
线程三正在工作中……19
线程二正在工作中……40
线程一正在工作中……21
线程二正在工作中……41
线程三正在工作中……20
线程二正在工作中……42
线程一正在工作中……22
线程二正在工作中……43
线程三正在工作中……21
线程二正在工作中……44
线程一正在工作中……23
线程二正在工作中……45
线程三正在工作中……22
线程二正在工作中……46
线程一正在工作中……24
线程二正在工作中……47
线程三正在工作中……23
线程二正在工作中……48
线程一正在工作中……25
线程二正在工作中……49
线程三正在工作中……24
线程一正在工作中……26
线程三正在工作中……25
线程一正在工作中……27
线程三正在工作中……26
线程三正在工作中……27
线程一正在工作中……28
线程三正在工作中……28
线程一正在工作中……29
线程一正在工作中……30
线程三正在工作中……29
线程一正在工作中……31
线程三正在工作中……30
线程一正在工作中……32
线程一正在工作中……33
线程三正在工作中……31
线程一正在工作中……34
线程三正在工作中……32
线程一正在工作中……35
线程三正在工作中……33
线程一正在工作中……36
线程三正在工作中……34
线程一正在工作中……37
线程三正在工作中……35
线程一正在工作中……38
线程三正在工作中……36
线程一正在工作中……39
线程三正在工作中……37
线程一正在工作中……40
线程三正在工作中……38
线程一正在工作中……41
线程三正在工作中……39
线程三正在工作中……40
线程一正在工作中……42
线程三正在工作中……41
线程一正在工作中……43
线程三正在工作中……42
线程一正在工作中……44
线程三正在工作中……43
线程一正在工作中……45
线程三正在工作中……44
线程一正在工作中……46
线程三正在工作中……45
线程一正在工作中……47
线程三正在工作中……46
线程一正在工作中……48
线程三正在工作中……47
线程一正在工作中……49
线程三正在工作中……48
线程三正在工作中……49

Thread类并非按照顺序执行,而是以一种随机交替的方式执行的,每一次的结果都不一样
线程启动后会默认调用run()方法
start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。
从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。
Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。

实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。

3.实现Runnable类

因为Thread类具有缺陷:面向对象的单继承局限,因此多实用Runnable接口来实现多线程

public class RunnableTest implements Runnable{

		private String name;
		public RunnableTest(String name) {
			this.name = name;
		}
		public void run() {
			for(int i = 0 ; i<50 ;i++) {
				System.out.println(this.name + " 正在执行中……" + i);
			}
		}
	

	public static void main(String[] args) {
				// 实例化继承Runnable接口的MyThread类
				Runnable mt1 = new RunnableTest("线程一");
				Runnable mt2 = new RunnableTest("线程二");
				Runnable mt3 = new RunnableTest("线程三");
				
				// 多线程启动
				new Thread(mt1).start();
				new Thread(mt2).start(); 		
				new Thread(mt3).start(); 		
			}
	}

输出结果:

线程二 正在执行中……0
线程二 正在执行中……1
线程一 正在执行中……0
线程三 正在执行中……0
线程一 正在执行中……1
线程二 正在执行中……2
线程一 正在执行中……2
线程三 正在执行中……1
线程一 正在执行中……3
线程二 正在执行中……3
线程一 正在执行中……4
线程三 正在执行中……2
线程一 正在执行中……5
线程二 正在执行中……4
线程一 正在执行中……6
线程三 正在执行中……3
线程一 正在执行中……7
线程二 正在执行中……5
线程一 正在执行中……8
线程三 正在执行中……4
线程一 正在执行中……9
线程二 正在执行中……6
线程一 正在执行中……10
线程三 正在执行中……5
线程三 正在执行中……6
线程一 正在执行中……11
线程二 正在执行中……7
线程一 正在执行中……12
线程三 正在执行中……7
线程一 正在执行中……13
线程二 正在执行中……8
线程一 正在执行中……14
线程三 正在执行中……8
线程一 正在执行中……15
线程二 正在执行中……9
线程一 正在执行中……16
线程三 正在执行中……9
线程一 正在执行中……17
线程二 正在执行中……10
线程一 正在执行中……18
线程三 正在执行中……10
线程一 正在执行中……19
线程二 正在执行中……11
线程一 正在执行中……20
线程三 正在执行中……11
线程一 正在执行中……21
线程二 正在执行中……12
线程一 正在执行中……22
线程三 正在执行中……12
线程一 正在执行中……23
线程二 正在执行中……13
线程一 正在执行中……24
线程二 正在执行中……14
线程三 正在执行中……13
线程二 正在执行中……15
线程一 正在执行中……25
线程二 正在执行中……16
线程三 正在执行中……14
线程二 正在执行中……17
线程一 正在执行中……26
线程二 正在执行中……18
线程三 正在执行中……15
线程二 正在执行中……19
线程一 正在执行中……27
线程二 正在执行中……20
线程三 正在执行中……16
线程二 正在执行中……21
线程一 正在执行中……28
线程二 正在执行中……22
线程三 正在执行中……17
线程二 正在执行中……23
线程一 正在执行中……29
线程二 正在执行中……24
线程三 正在执行中……18
线程二 正在执行中……25
线程一 正在执行中……30
线程二 正在执行中……26
线程三 正在执行中……19
线程二 正在执行中……27
线程一 正在执行中……31
线程二 正在执行中……28
线程三 正在执行中……20
线程二 正在执行中……29
线程一 正在执行中……32
线程二 正在执行中……30
线程三 正在执行中……21
线程二 正在执行中……31
线程一 正在执行中……33
线程二 正在执行中……32
线程三 正在执行中……22
线程二 正在执行中……33
线程一 正在执行中……34
线程二 正在执行中……34
线程三 正在执行中……23
线程二 正在执行中……35
线程一 正在执行中……35
线程二 正在执行中……36
线程三 正在执行中……24
线程二 正在执行中……37
线程一 正在执行中……36
线程二 正在执行中……38
线程三 正在执行中……25
线程二 正在执行中……39
线程一 正在执行中……37
线程二 正在执行中……40
线程一 正在执行中……38
线程三 正在执行中……26
线程一 正在执行中……39
线程二 正在执行中……41
线程一 正在执行中……40
线程三 正在执行中……27
线程一 正在执行中……41
线程二 正在执行中……42
线程二 正在执行中……43
线程三 正在执行中……28
线程二 正在执行中……44
线程一 正在执行中……42
线程二 正在执行中……45
线程三 正在执行中……29
线程二 正在执行中……46
线程一 正在执行中……43
线程二 正在执行中……47
线程三 正在执行中……30
线程二 正在执行中……48
线程一 正在执行中……44
线程二 正在执行中……49
线程三 正在执行中……31
线程一 正在执行中……45
线程一 正在执行中……46
线程一 正在执行中……47
线程三 正在执行中……32
线程三 正在执行中……33
线程一 正在执行中……48
线程三 正在执行中……34
线程一 正在执行中……49
线程三 正在执行中……35
线程三 正在执行中……36
线程三 正在执行中……37
线程三 正在执行中……38
线程三 正在执行中……39
线程三 正在执行中……40
线程三 正在执行中……41
线程三 正在执行中……42
线程三 正在执行中……43
线程三 正在执行中……44
线程三 正在执行中……45
线程三 正在执行中……46
线程三 正在执行中……47
线程三 正在执行中……48
线程三 正在执行中……49

以上程序实例化三个继承Runnable接口的MyThread类,然后通过Thread类的一个构造函数public Thread(Runnable target)分别实例化Thread类,再利用start()方法实现多线程。

继承Thread和实现Runnable的区别:

// Thread类定义:
public class Thread extends Object implements Runnable {}

// Runnable类定义:
public class RunnableTest implements Runnable{}

  • 也就是说,Thread类是Runable接口的子类,通过直接覆写Thread类的run方法实际上依然是覆写Runnable接口内的run方法,其实本质上是没有区别。

  • run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。

  • 在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码

  • 实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

  • 实现Runnable接口比继承Thread类所具有的优势:

    • 1):适合多个相同的程序代码的线程去处理同一个资源

    • 2):可以避免java中的单继承的限制

    • 3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

    • 4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

4.实现Callable类(仅做了解):

使用Runnable接口实现的多线程可以避免单继承的局限,但是还有一个问题就是run方法没有返回值,为此提供了一个Callable接口。
定义为:

@FunctionalIterface
public interface Callable<T>{
	public T call() throws Exception;
}

FuturaTask类常用方法:

import java.util.concurrent.ExecutionException; // 导入ExecutionException异常包
public FutureTask(Callable<T> callable) // 构造函数:接收Callable接口实例
public FutureTask(Runable runnable,T result) // 构造函数:接收Runnable接口实例,同时指定返回结果类型 
public T get() throws InterruptedException,ExecutionException // 取得线程操作返回结果

Thread类的一个构造方法:

public Thread(FutureTask<T> futuretask) //构造方法:接收FutureTask实例化对象

Callable接口实现采用泛型技术实现,继承需要重写call方法,再通过FutureTask包装器包装,传入后实例化Thread类实现多线程。
其中FutureTask类是Runnable接口的子类,所以才可以利用Thread类的start方法启动多线程

初识并发问题:

买火车票的例子:

public class newTest implements Runnable{
	
	private int ticknumber = 10;

	@Override
	public void run() {
		while(true) {
			if(ticknumber <= 0) {
				break;
			}
			System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumber-- + "票");
		}
		
	}
	public static void main(String[] args) {
		newTest ticket = new newTest();
		
		new Thread(ticket,"小明").start();
		new Thread(ticket,"小红").start();
		new Thread(ticket,"小军").start();
	}
}

输出结果:

小明-->拿到了第10票
小军-->拿到了第10票
小军-->拿到了第7票
小红-->拿到了第9票
小军-->拿到了第6票
小明-->拿到了第8票
小军-->拿到了第4票
小红-->拿到了第5票
小军-->拿到了第2票
小明-->拿到了第3票
小红-->拿到了第1

发现问题:
多个线程操作同一个资源的时候,线程不安全,数据紊乱。

6.线程的命名及获取

线程是不确定的运行状态,名称就是线程的主要标记。值得注意的是,对于线程的名字一定要在启动之前设置进程名称,不建议对已经启动的线程,进行更改名称,或者为不同线程设置重名。

主要方法

方法作用
public Thread(Runnable runnable,String name)构造函数:实例化线程对象,为线程对象设置名称
public final void setName(String name)普通函数:设置线程名字
public final String getName()普通函数:获取线程名字
	class MyThread implements Runnable{
		@Override
		public void run() {
			System.out.println( Thread.currentThread().getName());
			System.out.println( Thread.currentThread().getName());
		}
	}

	public class createName {
		public static void main(String[] args) {
			MyThread mt1 = new MyThread();
			MyThread mt2 = new MyThread();
			MyThread mt3 = new MyThread();
			
			new Thread(mt1,"线程一").start();
			new Thread(mt2,"线程二").start();
			new Thread(mt3).start(); // 使用默认的线程名称
			mt1.run(); // 这里直接查看main方法的线程名称
		}
	

输出结果

main
main
Thread-0
Thread-0
线程二
线程二
线程一
线程一

7.线程休眠

sleep方法定义在java.lang.Thread中,由Thread.sleep()调用实现。其作用是需要暂缓线程的执行速度,则可以让当前线程休眠,即当前线程从“运行状态”进入到“阻塞状态”。sleep方法会指定休眠时间,线程休眠的时间会大于或等于该休眠时间,该线程会被唤醒,此时它会由“阻塞状态”变成“就绪状态”,然后等待CPU的调度执行。
主要方法:

方法作用
public static void sleep(long millis) throws InterruptedException普通函数:设置休眠时间的毫秒数
public static void sleep(long millis,int nanos) throws InterruptedException普通函数:设置休眠毫秒数和纳秒数
public class restTime implements Runnable{
		public void run() {
			for(int i = 0 ; i<100 ; i++) {
				System.out.println(Thread.currentThread().getName() + " 正在工作中……" + i );
				if(i == 20) {
					try {
						System.out.println(Thread.currentThread().getName() + " 马上要休息五秒钟了……");
						Thread.sleep(5000); // 当前线程休眠五秒钟
						 System.out.println(Thread.currentThread().getName() + " 已经休息五秒钟了……");
					}catch (InterruptedException e) {
						System.out.println(Thread.currentThread().getName() + " 休眠被打扰了……");
					}
				}
			}
		}
public static void main(String[] args){
				 restTime mt1 = new restTime();
				 restTime mt2 = new restTime();
				 restTime mt3 = new restTime();
				 
				 new Thread(mt1,"线程一").start();
				 new Thread(mt2,"线程二").start();
				 new Thread(mt3,"线程三").start();
			}
		}

输出结果

线程一 正在工作中……0
线程二 正在工作中……0
线程二 正在工作中……1
线程三 正在工作中……0
线程二 正在工作中……2
线程一 正在工作中……1
线程二 正在工作中……3
线程一 正在工作中……2
线程三 正在工作中……1
线程一 正在工作中……3
线程二 正在工作中……4
线程一 正在工作中……4
线程三 正在工作中……2
线程一 正在工作中……5
线程二 正在工作中……5
线程一 正在工作中……6
线程三 正在工作中……3
线程一 正在工作中……7
线程二 正在工作中……6
线程一 正在工作中……8
线程三 正在工作中……4
线程一 正在工作中……9
线程一 正在工作中……10
线程二 正在工作中……7
线程一 正在工作中……11
线程三 正在工作中……5
线程一 正在工作中……12
线程一 正在工作中……13
线程二 正在工作中……8
线程一 正在工作中……14
线程三 正在工作中……6
线程一 正在工作中……15
线程二 正在工作中……9
线程一 正在工作中……16
线程三 正在工作中……7
线程一 正在工作中……17
线程二 正在工作中……10
线程一 正在工作中……18
线程三 正在工作中……8
线程一 正在工作中……19
线程二 正在工作中……11
线程一 正在工作中……20
线程三 正在工作中……9
线程一 马上要休息五秒钟了……
线程二 正在工作中……12
线程三 正在工作中……10
线程三 正在工作中……11
线程二 正在工作中……13
线程二 正在工作中……14
线程三 正在工作中……12
线程二 正在工作中……15
线程三 正在工作中……13
线程二 正在工作中……16
线程三 正在工作中……14
线程二 正在工作中……17
线程三 正在工作中……15
线程二 正在工作中……18
线程三 正在工作中……16
线程二 正在工作中……19
线程三 正在工作中……17
线程二 正在工作中……20
线程二 马上要休息五秒钟了……
线程三 正在工作中……18
线程三 正在工作中……19
线程三 正在工作中……20
线程三 马上要休息五秒钟了……
线程三 已经休息五秒钟了……
线程二 已经休息五秒钟了……
线程一 已经休息五秒钟了……
线程二 正在工作中……21
线程三 正在工作中……21
线程二 正在工作中……22
线程一 正在工作中……21
线程二 正在工作中……23
线程三 正在工作中……22
线程二 正在工作中……24
线程一 正在工作中……22
线程二 正在工作中……25
线程三 正在工作中……23
线程二 正在工作中……26
线程二 正在工作中……27
线程一 正在工作中……23
线程二 正在工作中……28
线程三 正在工作中……24
线程二 正在工作中……29
线程一 正在工作中……24
线程一 正在工作中……25
线程一 正在工作中……26
线程一 正在工作中……27
线程一 正在工作中……28
线程二 正在工作中……30
线程三 正在工作中……25
线程二 正在工作中……31
线程一 正在工作中……29
线程二 正在工作中……32
线程三 正在工作中……26
线程二 正在工作中……33
线程一 正在工作中……30
线程二 正在工作中……34
线程三 正在工作中……27
线程二 正在工作中……35
线程一 正在工作中……31
线程三 正在工作中……28
线程一 正在工作中……32
线程二 正在工作中……36
线程一 正在工作中……33
线程三 正在工作中……29
线程一 正在工作中……34
线程二 正在工作中……37
线程一 正在工作中……35
线程三 正在工作中……30
线程一 正在工作中……36
线程二 正在工作中……38
线程三 正在工作中……31
线程三 正在工作中……32
线程一 正在工作中……37
线程二 正在工作中……39
线程一 正在工作中……38
线程三 正在工作中……33
线程一 正在工作中……39
线程二 正在工作中……40
线程一 正在工作中……40
线程三 正在工作中……34
线程一 正在工作中……41
线程二 正在工作中……41
线程一 正在工作中……42
线程二 正在工作中……42
线程三 正在工作中……35
线程二 正在工作中……43
线程一 正在工作中……43
线程二 正在工作中……44
线程三 正在工作中……36
线程二 正在工作中……45
线程三 正在工作中……37
线程一 正在工作中……44
线程三 正在工作中……38
线程二 正在工作中……46
线程三 正在工作中……39
线程一 正在工作中……45
线程三 正在工作中……40
线程二 正在工作中……47
线程三 正在工作中……41
线程一 正在工作中……46
线程三 正在工作中……42
线程二 正在工作中……48
线程三 正在工作中……43
线程一 正在工作中……47
线程三 正在工作中……44
线程三 正在工作中……45
线程二 正在工作中……49
线程三 正在工作中……46
线程三 正在工作中……47
线程一 正在工作中……48
线程三 正在工作中……48
线程二 正在工作中……50
线程三 正在工作中……49
线程三 正在工作中……50
线程一 正在工作中……49
线程一 正在工作中……50
线程三 正在工作中……51
线程二 正在工作中……51
线程三 正在工作中……52
线程三 正在工作中……53
线程一 正在工作中……51
线程三 正在工作中……54
线程二 正在工作中……52
线程三 正在工作中……55
线程一 正在工作中……52
线程三 正在工作中……56
线程二 正在工作中……53
线程三 正在工作中……57
线程一 正在工作中……53
线程三 正在工作中……58
线程二 正在工作中……54
线程三 正在工作中……59
线程一 正在工作中……54
线程三 正在工作中……60
线程二 正在工作中……55
线程三 正在工作中……61
线程一 正在工作中……55
线程三 正在工作中……62
线程二 正在工作中……56
线程三 正在工作中……63
线程一 正在工作中……56
线程三 正在工作中……64
线程三 正在工作中……65
线程二 正在工作中……57
线程三 正在工作中……66
线程一 正在工作中……57
线程三 正在工作中……67
线程二 正在工作中……58
线程三 正在工作中……68
线程一 正在工作中……58
线程三 正在工作中……69
线程二 正在工作中……59
线程三 正在工作中……70
线程一 正在工作中……59
线程三 正在工作中……71
线程二 正在工作中……60
线程三 正在工作中……72
线程一 正在工作中……60
线程三 正在工作中……73
线程二 正在工作中……61
线程三 正在工作中……74
线程一 正在工作中……61
线程三 正在工作中……75
线程二 正在工作中……62
线程三 正在工作中……76
线程一 正在工作中……62
线程三 正在工作中……77
线程二 正在工作中……63
线程三 正在工作中……78
线程三 正在工作中……79
线程一 正在工作中……63
线程一 正在工作中……64
线程三 正在工作中……80
线程二 正在工作中……64
线程三 正在工作中……81
线程一 正在工作中……65
线程三 正在工作中……82
线程二 正在工作中……65
线程三 正在工作中……83
线程一 正在工作中……66
线程三 正在工作中……84
线程二 正在工作中……66
线程三 正在工作中……85
线程一 正在工作中……67
线程三 正在工作中……86
线程三 正在工作中……87
线程一 正在工作中……68
线程一 正在工作中……69
线程二 正在工作中……67
线程一 正在工作中……70
线程一 正在工作中……71
线程三 正在工作中……88
线程一 正在工作中……72
线程一 正在工作中……73
线程二 正在工作中……68
线程一 正在工作中……74
线程一 正在工作中……75
线程三 正在工作中……89
线程一 正在工作中……76
线程二 正在工作中……69
线程一 正在工作中……77
线程三 正在工作中……90
线程一 正在工作中……78
线程二 正在工作中……70
线程一 正在工作中……79
线程三 正在工作中……91
线程一 正在工作中……80
线程二 正在工作中……71
线程一 正在工作中……81
线程三 正在工作中……92
线程一 正在工作中……82
线程二 正在工作中……72
线程一 正在工作中……83
线程三 正在工作中……93
线程一 正在工作中……84
线程二 正在工作中……73
线程一 正在工作中……85
线程三 正在工作中……94
线程一 正在工作中……86
线程二 正在工作中……74
线程一 正在工作中……87
线程三 正在工作中……95
线程一 正在工作中……88
线程二 正在工作中……75
线程一 正在工作中……89
线程三 正在工作中……96
线程一 正在工作中……90
线程一 正在工作中……91
线程一 正在工作中……92
线程一 正在工作中……93
线程一 正在工作中……94
线程一 正在工作中……95
线程一 正在工作中……96
线程一 正在工作中……97
线程一 正在工作中……98
线程一 正在工作中……99
线程二 正在工作中……76
线程三 正在工作中……97
线程三 正在工作中……98
线程三 正在工作中……99
线程二 正在工作中……77
线程二 正在工作中……78
线程二 正在工作中……79
线程二 正在工作中……80
线程二 正在工作中……81
线程二 正在工作中……82
线程二 正在工作中……83
线程二 正在工作中……84
线程二 正在工作中……85
线程二 正在工作中……86
线程二 正在工作中……87
线程二 正在工作中……88
线程二 正在工作中……89
线程二 正在工作中……90
线程二 正在工作中……91
线程二 正在工作中……92
线程二 正在工作中……93
线程二 正在工作中……94
线程二 正在工作中……95
线程二 正在工作中……96
线程二 正在工作中……97
线程二 正在工作中……98
线程二 正在工作中……99

8.线程中止

interrupt方法定义在java.lang.Thread中,由Thread.interrupt()调用实现。该方法将会设置该线程的中断状态位,即设置为true,中断的结果线程是终止状态、还是阻塞状态或是继续运行至下一步,就取决于该程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(即中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。

方法作用
public boolean isInterrupted()普通函数:判断线程是否被中断
public void interrupt()普通函数:中断线程执行
public class MyThread implements Runnable {
	@Override
	public void run() {
		int i = 0;
		while(true) {
			System.out.println(Thread.currentThread().getName() + " 正在努力工作中……" + i++);
			try {
				System.out.println(Thread.currentThread().getName() + " 准备休息5秒钟了……");
				Thread.sleep(5000);  // 休眠5秒钟
				System.out.println(Thread.currentThread().getName() + " 已经休息5秒钟了……");
			}catch(InterruptedException e) {
				System.out.println(Thread.currentThread().getName() + " 被打扰了,不想工作了……");
				break;
			}
		}
	}
}

输出结果

线程一 正在努力工作中……0
线程三 正在努力工作中……0
线程二 正在努力工作中……0
线程三 准备休息5秒钟了……
线程一 准备休息5秒钟了……
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程二 已经休息5秒钟了……
线程一 已经休息5秒钟了……
线程二 正在努力工作中……1
线程三 正在努力工作中……1
线程二 准备休息5秒钟了……
线程一 正在努力工作中……1
线程一 准备休息5秒钟了……
线程三 准备休息5秒钟了……
吵闹~~~
线程一 不想干啦!!!!
线程三 已经休息5秒钟了……
线程三 正在努力工作中……2
线程二 已经休息5秒钟了……
线程三 准备休息5秒钟了……
线程二 正在努力工作中……2
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……3
线程三 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程二 正在努力工作中……3
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……4
线程二 已经休息5秒钟了……
线程三 准备休息5秒钟了……
线程二 正在努力工作中……4
线程二 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……5
线程三 准备休息5秒钟了……
线程二 正在努力工作中……5
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程二 已经休息5秒钟了……
线程三 正在努力工作中……6
线程二 正在努力工作中……6
线程三 准备休息5秒钟了……
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……7
线程三 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程二 正在努力工作中……7
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……8
线程三 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程二 正在努力工作中……8
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……9
线程三 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程二 正在努力工作中……9
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……10
线程三 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程二 正在努力工作中……10
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……11
线程三 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程二 正在努力工作中……11
线程二 准备休息5秒钟了……
线程三 已经休息5秒钟了……
线程三 正在努力工作中……12
线程三 准备休息5秒钟了……
线程二 已经休息5秒钟了……
线程二 正在努力工作中……12
线程二 准备休息5秒钟了……

9.线程礼让

yield方法定义在java.lang.Thread中,由Thread.yield()调用实现。多线程在彼此交替执行的时候往往需要进行资源的轮流抢占,如果某些不是很重要的线程抢占到资源但是又不急于执行时,就可以将当前的资源暂时让步出去,交给其它资源先执行。但是,因为yeild是将线程由“运行状态”转别为“就绪状态”,这样并不能保证在当前线程调用yield方法之后,其它具有相同优先级的线程就一定能获得执行权,也有可能是当前线程又进入到“运行状态”继续运行,因为还是要依靠CPU调度才可以。

其主要方法为:public static void yield() // 静态函数:线程让步

package threadTest;

public class compromise implements Runnable{
	private Thread thread = null;
	public compromise () {}
	public compromise(Thread thread) {
		this.thread = thread;
	}
	@Override
	public void run() {
		for(int i = 0 ; i<50 ; i++) {
			System.out.println(Thread.currentThread().getName()  + " 痛苦打工中……" + i);
			if( i  == 30) {
				System.out.println(Thread.currentThread().getName() + " 开始摸鱼把活给 "+thread.getName() + "了……");
				Thread.yield(); // 当前线程让步出去
				System.out.println(Thread.currentThread().getName() + " 再摸鱼就要被骂了打工打工……");
			}
		}
	}

	public static void main(String[] args) {
		Thread mainThread = Thread.currentThread();
		compromise mt1 = new compromise(mainThread);
		Thread thread1 = new Thread(mt1,"子线程");
		
		thread1.start();
		for(int i = 0 ; i < 40 ; i++) {
			System.out.println(Thread.currentThread().getName() + " 兢兢业业打工中……" + i);
		}
	}
}


输出结果:

main 兢兢业业打工中……0
main 兢兢业业打工中……1
main 兢兢业业打工中……2
main 兢兢业业打工中……3
main 兢兢业业打工中……4
子线程 痛苦打工中……0
子线程 痛苦打工中……1
main 兢兢业业打工中……5
子线程 痛苦打工中……2
main 兢兢业业打工中……6
子线程 痛苦打工中……3
main 兢兢业业打工中……7
子线程 痛苦打工中……4
main 兢兢业业打工中……8
子线程 痛苦打工中……5
子线程 痛苦打工中……6
main 兢兢业业打工中……9
子线程 痛苦打工中……7
main 兢兢业业打工中……10
子线程 痛苦打工中……8
main 兢兢业业打工中……11
main 兢兢业业打工中……12
子线程 痛苦打工中……9
main 兢兢业业打工中……13
子线程 痛苦打工中……10
main 兢兢业业打工中……14
子线程 痛苦打工中……11
main 兢兢业业打工中……15
子线程 痛苦打工中……12
main 兢兢业业打工中……16
子线程 痛苦打工中……13
main 兢兢业业打工中……17
子线程 痛苦打工中……14
main 兢兢业业打工中……18
子线程 痛苦打工中……15
main 兢兢业业打工中……19
子线程 痛苦打工中……16
main 兢兢业业打工中……20
子线程 痛苦打工中……17
main 兢兢业业打工中……21
子线程 痛苦打工中……18
main 兢兢业业打工中……22
子线程 痛苦打工中……19
main 兢兢业业打工中……23
子线程 痛苦打工中……20
子线程 痛苦打工中……21
子线程 痛苦打工中……22
main 兢兢业业打工中……24
子线程 痛苦打工中……23
main 兢兢业业打工中……25
子线程 痛苦打工中……24
main 兢兢业业打工中……26
子线程 痛苦打工中……25
main 兢兢业业打工中……27
子线程 痛苦打工中……26
main 兢兢业业打工中……28
子线程 痛苦打工中……27
main 兢兢业业打工中……29
子线程 痛苦打工中……28
main 兢兢业业打工中……30
子线程 痛苦打工中……29
main 兢兢业业打工中……31
子线程 痛苦打工中……30
main 兢兢业业打工中……32
子线程 开始摸鱼把活给 main了……
main 兢兢业业打工中……33
子线程 再摸鱼就要被骂了打工打工……
main 兢兢业业打工中……34
子线程 痛苦打工中……31
main 兢兢业业打工中……35
子线程 痛苦打工中……32
main 兢兢业业打工中……36
子线程 痛苦打工中……33
main 兢兢业业打工中……37
子线程 痛苦打工中……34
main 兢兢业业打工中……38
子线程 痛苦打工中……35
main 兢兢业业打工中……39
子线程 痛苦打工中……36
子线程 痛苦打工中……37
子线程 痛苦打工中……38
子线程 痛苦打工中……39
子线程 痛苦打工中……40
子线程 痛苦打工中……41
子线程 痛苦打工中……42
子线程 痛苦打工中……43
子线程 痛苦打工中……44
子线程 痛苦打工中……45
子线程 痛苦打工中……46
子线程 痛苦打工中……47
子线程 痛苦打工中……48
子线程 痛苦打工中……49

10.线程强制执行

join方法定义在java.lang.Thread中,由Thread.join()调用实现。多线程启动后会交替进行资源抢占和线程体执行,如果此时某些线程异常重要,也就是说这个对象需要优先执行完成,则可以设置为线程强制执行,待其完成后其它线程继续执行。

主要方法为:public final void join() throws InterruptedException //普通函数:强制执行

package threadTest;

public class Enforcemen implements Runnable{
	private Thread thread = null;
	public Enforcemen() {}
	public Enforcemen(Thread thread) {
		this.thread = thread;
	}
	@Override
	public void run() {
		for(int i = 0; i<50 ; i++) {
			if(i >= 20 && i <= 25) {
					try {
						System.out.println(thread.getName()  + "被强迫来打 " + Thread.currentThread().getName() + " 的工作了……" +i);
						thread.join();
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
			}
			// 以下语句不管上面判断语句是否执行都会执行的
			System.out.println(Thread.currentThread().getName() + " 痛苦打工中……" +i);
		}
	}

	public static void main(String[] args) {
		Thread mainThread = Thread.currentThread();
		Enforcemen mt1 = new Enforcemen(mainThread);
		Thread thread1 = new Thread(mt1,"子线程");
		
		thread1.start();
		for(int i = 0 ;i<20;i++) {
			try {
				Thread.sleep(1000); // 每次main线程休眠1秒
				System.out.println(Thread.currentThread().getName() +  "痛苦打工中……" + i);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("我!main!天选之子!位面之子!气运之子!今天竟然被" + Thread.currentThread().getName() + "强迫打工,导致我今天心情嘎嘎不爽,从此再也不能姬霓太美了┭┮﹏┭┮");
	}
}


输出结果:

子线程 痛苦打工中……0
子线程 痛苦打工中……1
子线程 痛苦打工中……2
子线程 痛苦打工中……3
子线程 痛苦打工中……4
子线程 痛苦打工中……5
子线程 痛苦打工中……6
子线程 痛苦打工中……7
子线程 痛苦打工中……8
子线程 痛苦打工中……9
子线程 痛苦打工中……10
子线程 痛苦打工中……11
子线程 痛苦打工中……12
子线程 痛苦打工中……13
子线程 痛苦打工中……14
子线程 痛苦打工中……15
子线程 痛苦打工中……16
子线程 痛苦打工中……17
子线程 痛苦打工中……18
子线程 痛苦打工中……19
main被强迫来打 子线程 的工作了……20
main痛苦打工中……0
main痛苦打工中……1
main痛苦打工中……2
main痛苦打工中……3
main痛苦打工中……4
main痛苦打工中……5
main痛苦打工中……6
main痛苦打工中……7
main痛苦打工中……8
main痛苦打工中……9
main痛苦打工中……10
main痛苦打工中……11
main痛苦打工中……12
main痛苦打工中……13
main痛苦打工中……14
main痛苦打工中……15
main痛苦打工中……16
main痛苦打工中……17
main痛苦打工中……18
main痛苦打工中……19
我!main!天选之子!位面之子!气运之子!今天竟然被main强迫打工,导致我今天心情嘎嘎不爽,从此再也不能姬霓太美了┭┮﹏┭┮
子线程 痛苦打工中……20
main被强迫来打 子线程 的工作了……21
子线程 痛苦打工中……21
main被强迫来打 子线程 的工作了……22
子线程 痛苦打工中……22
main被强迫来打 子线程 的工作了……23
子线程 痛苦打工中……23
main被强迫来打 子线程 的工作了……24
子线程 痛苦打工中……24
main被强迫来打 子线程 的工作了……25
子线程 痛苦打工中……25
子线程 痛苦打工中……26
子线程 痛苦打工中……27
子线程 痛苦打工中……28
子线程 痛苦打工中……29
子线程 痛苦打工中……30
子线程 痛苦打工中……31
子线程 痛苦打工中……32
子线程 痛苦打工中……33
子线程 痛苦打工中……34
子线程 痛苦打工中……35
子线程 痛苦打工中……36
子线程 痛苦打工中……37
子线程 痛苦打工中……38
子线程 痛苦打工中……39
子线程 痛苦打工中……40
子线程 痛苦打工中……41
子线程 痛苦打工中……42
子线程 痛苦打工中……43
子线程 痛苦打工中……44
子线程 痛苦打工中……45
子线程 痛苦打工中……46
子线程 痛苦打工中……47
子线程 痛苦打工中……48
子线程 痛苦打工中……49

11.线程优先级

所有创造的线程都是子线程,所有的子线程在启动时都会保持同样的优先级权限,但是如果现在某些重要的线程希望可以优先抢占到资源并且先执行,就可以修改优先级权限来实现。
记住当线程的优先级没有指定时,所有线程都携带普通优先级。

注意:

  • 优先级用从1到10的范围的整数指定。10表示最高优先级,1表示最低优先级,5是普通优先级,也就是默认优先级。
  • 优先级相对最高的线程在执行时被给予优先权限。但是不能保证线程在启动时就进入运行状态。
  • 优先级越高越有可能先执行。
Column 1Column 2
public static final int MAX_PRIORITY静态常量:最高优先级,数值为10
public static final int NORM_PRIORITY静态常量:普通优先级,数值为5
public static final int MIN_PRIORITY静态常量:最低优先级,数值为1
public final void setPriority(int newPriority)普通函数:设置优先级
public final int getPriority()普通函数:获取优先级

12.线程同步

那么现在有一个问题就是:在某一时刻,这一份资源在某一线程发生改变的时候,其他线程正在执行的操作也会受到影响。
例子就如前面的5.初识并发问题中的售票问题一样。
我们在售票例子中加入一个线程休眠可以更加清晰地看出问题所在:

package threadTest;

public class newTest implements Runnable{
	
	private int ticknumber = 10;

	@Override
	public void run() {
		while(true) {
			if(ticknumber <= 0) {
				break;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumber-- + "票");
		}
		
	}
	public static void main(String[] args) {
		newTest ticket = new newTest();
		
		new Thread(ticket,"小明").start();
		new Thread(ticket,"小红").start();
		new Thread(ticket,"小军").start();
	}
}

输出结果:

小军-->拿到了第10票
小红-->拿到了第9票
小明-->拿到了第8票
小军-->拿到了第7票
小明-->拿到了第5票
小红-->拿到了第6票
小军-->拿到了第4票
小明-->拿到了第3票
小红-->拿到了第2票
小军-->拿到了第1票
小红-->拿到了第0票
小明-->拿到了第-1

票数本来应该大于等于0的,但是因为某一个A线程延迟,由于共享资源,此时可能其它某个线程已经将票数售完(ticket-- == 0),然后线程A继续执行就会使得票数小于0。要解决类似于上面的问题,就需要引入线程同步和死锁概念。

我们现在就首先引入线程同步概念:

  • 解决数据共享问题必须使用同步,所谓的同步就是指多个线程在同一个时间段内只能有一个线程执行指定的代码,其他线程要等待此线程完成之后才可以继续进行执行。
  • 在Java中提供有synchronized关键字以实现同步处理,同步的关键是要为代码加上“锁”。

锁的操作有三种:
1.同步代码块
2.同步方法
3.Lock实现

同步代码块

使用方式:

synchronized(需要同步的对象){
    需要同步的操作
}

package threadTest;

public class newTest implements Runnable{
	
	private int ticknumber = 40;

	@Override
	public void run() {
		while(true) {
			synchronized(this) {//同步代码块
			if(ticknumber <= 0) {
				System.out.println("没有票票啦~谁都没有拿到~~~");
				break;
			}
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumber-- + "票");
		}
		}
		
	}
	public static void main(String[] args) {
		
		newTest ticket = new newTest();
		
		new Thread(ticket,"小明").start();
		new Thread(ticket,"小红").start();
		new Thread(ticket,"小军").start();
	}
}

输出结果:

小明-->拿到了第40票
小明-->拿到了第39票
小明-->拿到了第38票
小明-->拿到了第37票
小明-->拿到了第36票
小明-->拿到了第35票
小明-->拿到了第34票
小明-->拿到了第33票
小明-->拿到了第32票
小明-->拿到了第31票
小明-->拿到了第30票
小明-->拿到了第29票
小明-->拿到了第28票
小明-->拿到了第27票
小明-->拿到了第26票
小明-->拿到了第25票
小明-->拿到了第24票
小明-->拿到了第23票
小明-->拿到了第22票
小明-->拿到了第21票
小明-->拿到了第20票
小明-->拿到了第19票
小明-->拿到了第18票
小明-->拿到了第17票
小明-->拿到了第16票
小明-->拿到了第15票
小明-->拿到了第14票
小明-->拿到了第13票
小明-->拿到了第12票
小明-->拿到了第11票
小明-->拿到了第10票
小明-->拿到了第9票
小军-->拿到了第8票
小军-->拿到了第7票
小军-->拿到了第6票
小军-->拿到了第5票
小军-->拿到了第4票
小军-->拿到了第3票
小军-->拿到了第2票
小军-->拿到了第1票
没有票票啦~谁都没有拿到~~~
没有票票啦~谁都没有拿到~~~
没有票票啦~谁都没有拿到~~~

同步方法实现

使用方法为:利用函数包装的形式实现
如下:
修饰符 synchronized 返回类型 函数名()

package threadTest;

public class newTest implements Runnable{
	
	private int ticknumber = 1150;

	@Override
	public void run() {
		while(this.sale()) {}
	}
	public synchronized boolean sale() {
		if(ticknumber <= 0) {
			System.out.println("没有票票啦~谁都没有拿到~~~");
			return false;
		}
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumber-- + "票");
			return true;
		}
	
	public static void main(String[] args) {
		
		newTest ticket = new newTest();
		
		new Thread(ticket,"小明").start();
		new Thread(ticket,"小红").start();
		new Thread(ticket,"小军").start();
	}
}

输出结果(部分):

小明-->拿到了第36票
小明-->拿到了第35票
小明-->拿到了第34票
小明-->拿到了第33票
小军-->拿到了第32票
小军-->拿到了第31票
小军-->拿到了第30票
小军-->拿到了第29票
小军-->拿到了第28票
小红-->拿到了第27票
小红-->拿到了第26票
小红-->拿到了第25票
小红-->拿到了第24票
……
小军-->拿到了第5票
小军-->拿到了第4票
小军-->拿到了第3票
小明-->拿到了第2票
小明-->拿到了第1票
没有票票啦~谁都没有拿到~~~
没有票票啦~谁都没有拿到~~~
没有票票啦~谁都没有拿到~~~

Lock锁实现:

package threadTest;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class newTest implements Runnable{
	
	private int ticknumber = 1150;
	private final Lock lock = new ReentrantLock();

	@Override
	public void run() {
		while(this.sale()) {}
	}
	public synchronized boolean sale() {
		lock.lock();
		try {
		if(ticknumber <= 0) {
			System.out.println("没有票票啦~谁都没有拿到~~~");
			return false;
		}
				Thread.sleep(10);
				System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumber-- + "票");
			}catch(Exception e) {
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
			return true;
		}
	
	
	public static void main(String[] args) {
		
		newTest ticket = new newTest();
		
		new Thread(ticket,"小明").start();
		new Thread(ticket,"小红").start();
		new Thread(ticket,"小军").start();
	}
}

输出结果(部分)

小明-->拿到了第115票
小明-->拿到了第114票
小明-->拿到了第113票
小明-->拿到了第112票
小军-->拿到了第111票
小军-->拿到了第110票
小红-->拿到了第109票
小红-->拿到了第108票
小红-->拿到了第107票
小红-->拿到了第106票
小红-->拿到了第105票
小红-->拿到了第104票
小红-->拿到了第103票
小军-->拿到了第102票
小军-->拿到了第101票
……
小军-->拿到了第5票
小军-->拿到了第4票
小军-->拿到了第3票
小军-->拿到了第2票
小军-->拿到了第1票
没有票票啦~谁都没有拿到~~~
没有票票啦~谁都没有拿到~~~
没有票票啦~谁都没有拿到~~~

13.程序死锁

所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,死锁的操作一般是在程序运行时候才有可能出现,死锁是在多线程开发中较为常见的一种问题,过多的同步就有可能出现死锁。

例如该例子:

package threadTest;

class firstCorssBridge{
	public synchronized void tell(secondCorssBridge scb) {
		System.out.println("张三告诉王五:我先过,你后过,否则你别想过这桥!");
		scb.cross();
	}
	// 以下函数不会执行
	public synchronized void cross() {
		System.out.println("张三快快乐乐地过桥了……");
	}
}
class secondCorssBridge{
	public synchronized void tell(firstCorssBridge fcb) {
		System.out.println("王五告诉张三:我先过,你后过,否则你别想过这桥!");
		fcb.cross();
	}
	// 以下函数不会执行
	public synchronized void cross() {
		System.out.println("王五快快乐乐地过桥了……");
	}
}


public class Deadlock implements Runnable{
	private firstCorssBridge fcb = new firstCorssBridge();
	private secondCorssBridge scb = new secondCorssBridge();
	
	public Deadlock() {
		// 启动线程 并执行以下语句
		new Thread(this).start(); // 会运行run函数
		fcb.tell(scb); // 运行到里面时 fcb会等待scb
	}
	@Override
	public void run() {
		scb.tell(fcb); // 运行到里面时 scb会等待fcb
	}
	
	public static void main(String[] args) {
		new Deadlock();
	}
}

输出结果:

张三告诉王五:我先过,你后过,否则你别想过这桥!
王五告诉张三:我先过,你后过,否则你别想过这桥!

两者相互之间产生死锁导致后续代码无法运行;
而在其中加入线程休眠之后(外力作用):

package threadTest;

class firstCorssBridge{
	public synchronized void tell(secondCorssBridge scb) {
		System.out.println("张三告诉王五:我先过,你后过,否则你别想过这桥!");
		scb.cross();
		
	}
	// 以下函数不会执行
	public synchronized void cross() {
		System.out.println("张三快快乐乐地过桥了……");
	}
}
class secondCorssBridge{
	public synchronized void tell(firstCorssBridge fcb) {
		System.out.println("王五告诉张三:我先过,你后过,否则你别想过这桥!");
		fcb.cross();
	}
	// 以下函数不会执行
	public synchronized void cross() {
		System.out.println("王五快快乐乐地过桥了……");
	}
}


public class Deadlock implements Runnable{
	private firstCorssBridge fcb = new firstCorssBridge();
	private secondCorssBridge scb = new secondCorssBridge();
	
	public Deadlock() {
		// 启动线程 并执行以下语句
		new Thread(this).start(); // 会运行run函数
		try {
			Thread.sleep(100); // 休眠0.1秒钟
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		fcb.tell(scb); // 运行到里面时 fcb会等待scb
	}
	@Override
	public void run() {
		scb.tell(fcb); // 运行到里面时 scb会等待fcb
	}
	
	public static void main(String[] args) {
		new Deadlock();
	}
}

输出结果为:

王五告诉张三:我先过,你后过,否则你别想过这桥!
张三快快乐乐地过桥了……
张三告诉王五:我先过,你后过,否则你别想过这桥!
王五快快乐乐地过桥了……

14.守护线程

Java中的线程分为两类,用户线程和守护线程。守护线程(Daemon)是一种运行在后台的线程服务线程,当用户线程存在时,守护线程可以同时存在,但是,当用户线程不存在时,守护线程会全部消失。

主要操作方法:

Column 1Column 2
public final setDaemon(boolean on) throws Exception普通函数:是否设置为守护线程
public final boolean isDaemon()普通函数: 判断是否为

守护线程的应用场景

在主线程关闭后无需手动关闭守护线程,因为会自动关闭,避免了麻烦,Java垃圾回收线程就是一个典型的守护线程,简单粗暴地可以理解为所有为线程服务而不涉及资源的线程都能设置为守护线程。

15.线程池

概念:容纳多个线程的容器,其中的线程可以反复使用,节省了反复创建线程而消耗的资源,同时也节省了操作。因为如果并发的线程数量过多,并且每个线程都是执行一个时间很短的任务就结束,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要消耗时间,线程也属于宝贵的系统资源,因此,线程池就是为了能使线程可以复用而创建的。
Rannable:

package threadTest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Threadpool {
    public static void main(String[] args) {
        // 1.创建一个线程池,指定线程的数量为4
        ExecutorService pools = Executors.newFixedThreadPool(4);
        // 2.添加线程任务
        Runnable target = new MyRunnable();
        pools.submit(target); // 第一次提交任务,此时创建新线程
        pools.submit(target); // 第二次提交任务,此时创建新线程
        pools.submit(target); // 第三次提交任务,此时创建新线程
        pools.submit(target); // 第四次提交任务,此时创建新线程
        pools.submit(target); // 第五次提交任务,复用之前的线程
        pools.shutdown(); // 当所有任务全部完成后才关闭线程池
//        pools.shutdownNow(); // 立即关闭线程池

    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i = 0 ; i<10 ; i++) {
            System.out.println(Thread.currentThread().getName()+"正在执行任务…  "+i);
        }
    }
}

输出结果:

pool-1-thread-3正在执行任务…  0
pool-1-thread-3正在执行任务…  1
pool-1-thread-1正在执行任务…  0
pool-1-thread-4正在执行任务…  0
pool-1-thread-2正在执行任务…  0
pool-1-thread-4正在执行任务…  1
pool-1-thread-1正在执行任务…  1
pool-1-thread-3正在执行任务…  2
pool-1-thread-1正在执行任务…  2
pool-1-thread-4正在执行任务…  2
pool-1-thread-2正在执行任务…  1
pool-1-thread-4正在执行任务…  3
pool-1-thread-1正在执行任务…  3
pool-1-thread-3正在执行任务…  3
pool-1-thread-1正在执行任务…  4
pool-1-thread-4正在执行任务…  4
pool-1-thread-2正在执行任务…  2
pool-1-thread-4正在执行任务…  5
pool-1-thread-1正在执行任务…  5
pool-1-thread-3正在执行任务…  4
pool-1-thread-1正在执行任务…  6
pool-1-thread-4正在执行任务…  6
pool-1-thread-2正在执行任务…  3
pool-1-thread-4正在执行任务…  7
pool-1-thread-1正在执行任务…  7
pool-1-thread-3正在执行任务…  5
pool-1-thread-1正在执行任务…  8
pool-1-thread-4正在执行任务…  8
pool-1-thread-2正在执行任务…  4
pool-1-thread-4正在执行任务…  9
pool-1-thread-1正在执行任务…  9
pool-1-thread-3正在执行任务…  6
pool-1-thread-2正在执行任务…  5
pool-1-thread-3正在执行任务…  7
pool-1-thread-2正在执行任务…  6
pool-1-thread-3正在执行任务…  8
pool-1-thread-2正在执行任务…  7
pool-1-thread-3正在执行任务…  9
pool-1-thread-2正在执行任务…  8
pool-1-thread-2正在执行任务…  9
pool-1-thread-2正在执行任务…  0
pool-1-thread-2正在执行任务…  1
pool-1-thread-2正在执行任务…  2
pool-1-thread-2正在执行任务…  3
pool-1-thread-2正在执行任务…  4
pool-1-thread-2正在执行任务…  5
pool-1-thread-2正在执行任务…  6
pool-1-thread-2正在执行任务…  7
pool-1-thread-2正在执行任务…  8
pool-1-thread-2正在执行任务…  9

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值