Java多线程编程并发编程

一、Java多线程编程并发编程

第一节 Java基础

线程状态

Java线程有6个状态定义:java.lang.Thread.State

  1. New:尚未启动的线程的线程状态;
  2. Runnable:可运行线程的线程状态,等待CPU调度;
  3. Blocked: 线程阻塞等待监视器锁定的线程状态,处于synchronized同步代码块或方法中被阻塞;
  4. Waiting:等待线程的线程状态,下列不带超时的方式:Object.wait、Thread.join、LockSupport.park;
  5. Timed Waiting:具有指定等待时间的等待线程的线程状态。带超时的方式:Thread.sleep、Object.wait、Thread.join、LockSupport.parkNanos、LockSupport.parkUntil;
  6. Terminated :终止线程的线程状态。线程正常完成执行或者出现异常。

状态流程图:
在这里插入图片描述

具体状态可以运行如下Demo进行简单的了解

/**
 * 示例2 - 多线程运行状态切换示例 <br/>
 */
public class Demo1 {
	public static Thread thread1;
	public static Demo1 obj;

	public static void main(String[] args) throws Exception {
		// 第一种状态切换 - 新建 -> 运行 -> 终止
		System.out.println("#######第一种状态切换  - 新建 -> 运行 -> 终止################################");
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("thread1当前状态:" + Thread.currentThread().getState().toString());
				System.out.println("thread1 执行了");
			}
		});
		System.out.println("没调用start方法,thread1当前状态:" + thread1.getState().toString());
		thread1.start();
		Thread.sleep(2000L); // 等待thread1执行结束,再看状态
		System.out.println("等待两秒,再看thread1当前状态:" + thread1.getState().toString());
		// thread1.start(); TODO 注意,线程终止之后,再进行调用,会抛出IllegalThreadStateException异常

		System.out.println();
		System.out.println("############第二种:新建 -> 运行 -> 等待 -> 运行 -> 终止(sleep方式)###########################");
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {// 将线程2移动到等待状态,1500后自动唤醒
					Thread.sleep(1500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("thread2当前状态:" + Thread.currentThread().getState().toString());
				System.out.println("thread2 执行了");
			}
		});
		System.out.println("没调用start方法,thread2当前状态:" + thread2.getState().toString());
		thread2.start();
		System.out.println("调用start方法,thread2当前状态:" + thread2.getState().toString());
		Thread.sleep(200L); // 等待200毫秒,再看状态
		System.out.println("等待200毫秒,再看thread2当前状态:" + thread2.getState().toString());
		Thread.sleep(3000L); // 再等待3秒,让thread2执行完毕,再看状态
		System.out.println("等待3秒,再看thread2当前状态:" + thread2.getState().toString());

		System.out.println();
		System.out.println("############第三种:新建 -> 运行 -> 阻塞 -> 运行 -> 终止###########################");
		Thread thread3 = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (Demo2.class) {
					System.out.println("thread3当前状态:" + Thread.currentThread().getState().toString());
					System.out.println("thread3 执行了");
				}
			}
		});
		synchronized (Demo2.class) {
			System.out.println("没调用start方法,thread3当前状态:" + thread3.getState().toString());
			thread3.start();
			System.out.println("调用start方法,thread3当前状态:" + thread3.getState().toString());
			Thread.sleep(200L); // 等待200毫秒,再看状态
			System.out.println("等待200毫秒,再看thread3当前状态:" + thread3.getState().toString());
		}
		Thread.sleep(3000L); // 再等待3秒,让thread3执行完毕,再看状态
		System.out.println("等待3秒,让thread3抢到锁,再看thread3当前状态:" + thread3.getState().toString());

	}
}

运行结果:

#######第一种状态切换  - 新建 -> 运行 -> 终止################################
没调用start方法,thread1当前状态:NEW
thread1当前状态:RUNNABLE
thread1 执行了
等待两秒,再看thread1当前状态:TERMINATED

############第二种:新建 -> 运行 -> 等待 -> 运行 -> 终止(sleep方式)###########################
没调用start方法,thread2当前状态:NEW
调用start方法,thread2当前状态:RUNNABLE
等待200毫秒,再看thread2当前状态:TIMED_WAITING
thread2当前状态:RUNNABLE
thread2 执行了
等待3秒,再看thread2当前状态:TERMINATED

############第三种:新建 -> 运行 -> 阻塞 -> 运行 -> 终止###########################
没调用start方法,thread3当前状态:NEW
调用start方法,thread3当前状态:RUNNABLE
等待200毫秒,再看thread3当前状态:BLOCKED
thread3当前状态:RUNNABLE
thread3 执行了
等待3秒,让thread3抢到锁,再看thread3当前状态:TERMINATED

线程中止

如何中止一个线程:

1、不正确的线程中止-Stop
a、Stop:中止线程,并且清除监控器锁的信息,但是可能导致线程安全,JDK不建议用。
b、Destroy:JDK未实现该方法。
线程不安全演示:


public class StopThread extends Thread {
	private int i = 0, j = 0;

	@Override
	public void run() {
		synchronized (this) {
			// 增加同步锁,确保线程安全
			++i;
			try {
				// 休眠10秒,模拟耗时操作
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			++j;
		}
	}

	/** * 打印i和j */
	public void print() {
		System.out.println("i=" + i + " j=" + j);
	}
}
/**
 * 示例3 - 线程stop强制性中止,破坏线程安全的示例
 */
public class Demo3 {
	public static void main(String[] args) throws InterruptedException {
		StopThread thread = new StopThread();
		thread.start();
		// 休眠1秒,确保i变量自增成功
		Thread.sleep(1000);
		// 暂停线程
		thread.stop(); // 错误的终止
		// thread.interrupt(); // 正确终止
		while (thread.isAlive()) {
			// 确保线程已经终止
		} // 输出结果
		thread.print();
	}
}

运行结果:
thread.stop()方式中止线程,会有线程安全的问题,导致实际运行结果:i=1 j=0,线程安全的前提下:i=1 j=1。
thread.interrupt()中止线程,程序会以抛出java.lang.InterruptedException异常来通知线程中止,捕捉该异常,当前线程会执行完后续操作,保证线程的安全性。

正确中止线程的方法:interrupt

二、通过标志位来中止线程
代码逻辑中,增加一个判断,用来控制线程执行的中止。

/** 通过状态位来判断 */
public class Demo4 extends Thread {
	
	public volatile static boolean flag = true;
	
	public static void main(String[] args) throws InterruptedException {
		new Thread(() -> {
			while (flag) { // 判断是否运行
				System.out.println("运行中");
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
		// 3秒之后,将状态标志改为False,代表不继续运行
		Thread.sleep(3000L);
		flag = false;
		System.out.println("程序运行结束");
	}
}

线程通信

1、文件共享
线程1将数据写入到文件系统,线程2可以通过读取该文件实现数据的共享

2、变量共享
利用内存的公共区域。如:定义共享变量,线程1对变量写入数据,线程2来读取数据。
3、线程协作–JDK API
JDK中对于需要多线程协作完成某一任务的场景,提供了对应API支持。
多线程协作的典型场景是:生产者 - 消费者模型。(线程阻塞
线程唤醒)
示例:线程1去买包子,没有包子,则不再执行。线程2生产出包子,通知线程1继续执行。

  1. suspend和resume
  2. wait和notify
  3. park和unpark

区别:
第一种容易产生死锁:
1、同步代码块中使用,由于suspend挂起后并不会释放锁,容易造成死锁;
2、执行顺序也可能造成死锁,suspend比resume后执行的情形;
这两个API已废弃。

/** 死锁的suspend/resume。 suspend并不会像wait一样释放锁,故此容易写出死锁代码 */
	public void suspendResumeDeadLockTest() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // 如果没包子,则进入等待
				System.out.println("1、进入等待");
				// 当前线程拿到锁,然后挂起
				synchronized (this) {
					Thread.currentThread().suspend();
				}
			}
			System.out.println("2、买到包子,回家");
		});
		consumerThread.start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		// 争取到锁以后,再恢复consumerThread
		synchronized (this) {
			consumerThread.resume();
		}
		System.out.println("3、通知消费者");
	}
/** 导致程序永久挂起的suspend/resume */
	public void suspendResumeDeadLockTest2() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) {
				System.out.println("1、没包子,进入等待");
				try { // 为这个线程加上一点延时
					Thread.sleep(5000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 这里的挂起执行在resume后面
				Thread.currentThread().suspend();
			}
			System.out.println("2、买到包子,回家");
		});
		consumerThread.start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		consumerThread.resume();
		System.out.println("3、通知消费者");
		consumerThread.join();
	}

第二种wait和notify这些方法只能由同一对象锁的持有者线程调用,也就是写在同步代码块里面,否则会抛出IllegalMonitorStateException异常。
wait方法会将当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。
notify/notifyAll方法会唤醒一个或所有正在等待这个对象锁的线程。
如果在notify被调用之后,才开始wait方法的调用,线程会永远处于waiting状态。

/** 正常的wait/notify */
	public void waitNotifyTest() throws Exception {
		// 对象中增加一个wait线程
		new Thread(()->{
			synchronized (this) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("我也被唤醒了");
			}
		}).start();
		// 启动线程
		new Thread(() -> {
				synchronized (this) {
					while (baozidian == null) { // 如果没包子,则进入等待
					try {
						System.out.println("1、进入等待");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2、买到包子,回家");
		}).start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		synchronized (this) {
			this.notifyAll();
			System.out.println("3、通知消费者");
		}
	}

	/** 会导致程序永久等待的wait/notify */
	public void waitNotifyDeadLockTest() throws Exception {
		// 启动线程
		new Thread(() -> {
			if (baozidian == null) { // 如果没包子,则进入等待
				try {
					Thread.sleep(5000L);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				synchronized (this) {
					try {
						System.out.println("1、进入等待");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2、买到包子,回家");
		}).start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		synchronized (this) {
			this.notifyAll();
			System.out.println("3、通知消费者");
		}
	}

第三种
1、unpark先执行唤醒,线程中的park后执行,该线程还能继续被唤醒,不会有先后顺序的问题;
2、同步代码块中不会释放锁

/** 正常的park/unpark */
	public void parkUnparkTest() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			while (baozidian == null) { // 如果没包子,则进入等待
				System.out.println("1、进入等待");
				try {
					// 模拟业务场景,主线程中的unpark先执行唤醒,线程中的park后执行,该线程还能继续被唤醒
					Thread.sleep(6000L);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				LockSupport.park();
			}
			System.out.println("2、买到包子,回家");
		});
		consumerThread.start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		LockSupport.unpark(consumerThread);
		System.out.println("3、通知消费者");
	}

	/** 死锁的park/unpark */
	public void parkUnparkDeadLockTest() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // 如果没包子,则进入等待
				System.out.println("1、进入等待");
				// 当前线程拿到锁,然后挂起
				synchronized (this) {
					LockSupport.park();
				}
			}
			System.out.println("2、买到包子,回家");
		});
		consumerThread.start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		// 争取到锁以后,再恢复consumerThread
		synchronized (this) {
			LockSupport.unpark(consumerThread);
		}
		System.out.println("3、通知消费者");
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生就业服务平台管理系统按照操作主体分为管理员和用户。管理员的功能包括学生档案管理、字典管理、试卷管理、试卷选题管理、试题表管理、考试记录表管理、答题详情表管理、错题表管理、法律法规管理、法律法规收藏管理、法律法规留言管理、就业分析管理、论坛管理、企业管理、简历管理、老师管理、简历投递管理、新闻资讯管理、新闻资讯收藏管理、新闻资讯留言管理、学生信息管理、宣传管理、学生管理、职位招聘管理、职位收藏管理、招聘咨询管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 大学生就业服务平台管理系统可以提高大学生就业服务平台信息管理问题的解决效率,优化大学生就业服务平台信息处理流程,保证大学生就业服务平台信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理新闻信息,管理大学生就业服务平台信息,包括考试管理,培训管理,投递管理,薪资管理等,可以管理新闻信息。 考试管理界面,管理员在考试管理界面中可以对界面中显示,可以对考试信息的考试状态进行查看,可以添加新的考试信息等。投递管理界面,管理员在投递管理界面中查看投递种类信息,投递描述信息,新增投递信息等。新闻信息管理界面,管理员在新闻信息管理界面中新增新闻信息,可以删除新闻信息。新闻信息类型管理界面,管理员在新闻信息类型管理界面查看新闻信息的工作状态,可以对新闻信息的数据进行导出,可以添加新新闻信息的信息,可以编辑新闻信息信息,删除新闻信息信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值