Java基础--并发编程基础(4)

1.死锁

线程同步的时候会对对象监视器所监视的操作上锁,也就是只有当前线程能够进来,其他线程是进不来的,除非你拿到了监视器。
死锁:当线程A进入到了X对象的监视器内,线程B进入到了Y对象的监视器内,X对象的监视器内部调用了Y对象监视器内部的操作,所以线程A想正常终结的话,必须等线程B交出监视器(终结或挂起(挂起不考虑)),然后以可重入锁的方式进入Y对象监视器的内部(可重入锁的概念,后续博文会解释),执行完相关操作,然后终结;而巧的是Y对象的监视器调用了X对象监视器内部的操作,B线程若是想正常终结的话,必须等A线程交出监视器,这样互相等待的状态称为死锁。
死锁案例:
public class DeadLockTest {

	public static void main(String[] args) {
		/*
		 * 所谓死锁,就是线程A进入到X对象的监视器内部,线程B进入到了Y对象的监视器内部,而A在X监视器内部需要调用B的监视器内部操作才能结束,所以就调用了,然而,得等B交出监视器(B终结或者挂起(挂起除外))
		 * 而巧的是B要想终结的话,得执行A监视器内部的操作才能执行,这样就造成了互相等待,而且不会有解,除非有一个能够不以终结的方式交出监视器
		 */
		Task1 t1 = new Task1();
		Task2 t2 = new Task2();
		new Thread(()->{
			t1.task1(t2);
		}).start();
		new Thread(()->{
			t2.task1(t1);
		}).start();
//		运行结果:
//		Task1...task1
//		Task2...task1
//		正在运行..(必须强行终止否则没有头啊)
	}
	
}
class Task1{
	public synchronized void task1(Task2 task2){
		System.out.println("Task1...task1");
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		task2.task2();
	}
	public synchronized void task2(){
		System.out.println("Task1...task2");
	}
}
class Task2{
	public synchronized void task1(Task1 task1){
		System.out.println("Task2...task1");
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		task1.task2();
	}
	public synchronized void task2(){
		System.out.println("Task2...task2");
	}
}

2.线程状态间转化

线程总共有5个状态:被创建后还没有被执行状态、执行状态、阻塞状态(等待,不交出锁)、等待状态、终结态
相互之间的转化图如下:
调用sleep方法后处于等待状态,但是不交出监视器持有;调用wait方法后也是处于等待状态,但是交出监视器持有。

3.线程组

线程组为管理一组线程提供了一种便利的方式,可以把一组线程当成一个单位进行管理。对于希望对一组线程进行操作(挂起、恢复等)是很便利的。
线程组类:ThreadGroup(String groupName) ThreadGroup(ThreadGroup parentGp,String groupName)
对于如何设置线程所属的线程组,以及如何获取线程组中的线程由下文的代码实例说明。
对于线程组的理解,可以从Java中线程的组织关系下手:
1.Java中线程是以树的结构进行管理的,树的根是主线程,由主线程创建的线程是根的孩子,由此递归
2.这颗树上的线程都是活着的线程:没有终结也没有挂起
3.线程组在这个树中是什么呢?线程组是非叶子节点。如果没有加入线程组的话,所有的子线程都是根(主线程)的孩子,加了线程组,那所有的线程(除主线程)都是叶子节点,从树的不同非叶子节点(线程组)总是能获取这个节点的所有子孙节点(子线程),说明代码如下:
实例代码如下:
public class ThreadGroupTest {

	public static void main(String[] args) {
		/*
		 * 对于任何一个程序,首先有一个主线程,这个主线程是程序主函数(入口函数)启动的时候启动的
		 * 由主线程M开的线程A之间的关系是:M和A位于同一个线程组(即使在开新线程的时候不指明线程组的话),M是A的父线程
		 * 同样,如果A线程再开线程,那开的线程就是A线程的子线程
		 * 可用的三个构造器分别为:
		 * Thread(ThreadGroup groupOb,Runnable threadOb):指明线程组和Runnable实现
		 * Thread(ThreadGroup groupOb,Runnable threadOb,String threadName):指明线程租和Runnable实现和线程名称
		 * Thread(ThreadGroup groupOb,String threadName):指明线程组和线程名称,通常在Thread子类的构造器中调用super方法时使用这个
		 * 引入线程组的目的是为了批管理线程
		 */
		//t1和主线程位于同一线程组
		Thread t1 = new Thread(()->{
			//t1_1和主线程位于同一线程组
			Thread t1_1 = new Thread(()->{
				//加上死循环是为了保证这个线程一直在这个线程组中活下去,已经终结的线程是不能通过线程组获取的
				while(true);
			});
			t1_1.start();
			while(true);
		});
		t1.start();
		//t2属于线程组X
		Thread t2 = new Thread(new ThreadGroup("X"),()->{
			Thread t2_2 = new Thread(new ThreadGroup("Y"),()->{
				while(true);
			});
			t2_2.start();
			while(true);
		});
		t2.start();
		//获取这两个线程租
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();
		//获取展示这两个线程组中都有哪些线程
		tg1.list();
		tg2.list();
		//分别获取到每个线程组中的每个线程
		Thread[] threadarray1 = new Thread[tg1.activeCount()];
		Thread[] threadarray2 = new Thread[tg2.activeCount()];
		tg1.enumerate(threadarray1);
		tg2.enumerate(threadarray2);
		//操作已经get到的线程组中的线程,这里仅仅是打印出来,实际就是这样进行批处理的哦
		System.out.println("Threads in the main Thread Group");
		for(Thread t:threadarray1){
			System.out.println(t);
		}
		System.out.println("Threads in the X Thread Group");
		for(Thread t:threadarray2){
			System.out.println(t);
		}
		
//		运行结果(带#表示不是运行结果,是后加的注释):
//		#树结构:
//		java.lang.ThreadGroup[name=main,maxpri=10]					#从根节点获取子线程
//			    Thread[main,5,main]
//			    Thread[Thread-0,5,main]
//			    java.lang.ThreadGroup[name=X,maxpri=10]
//			        Thread[Thread-1,5,X]
//			        java.lang.ThreadGroup[name=Y,maxpri=10]
//			            Thread[Thread-3,5,Y]
//			java.lang.ThreadGroup[name=X,maxpri=10]					#从X线程组获取子线程
//			    Thread[Thread-1,5,X]
//			    java.lang.ThreadGroup[name=Y,maxpri=10]
//			        Thread[Thread-3,5,Y]
//			#数组表示:
//			Threads in the main Thread Group
//			Thread[main,5,main]
//			Thread[Thread-0,5,main]
//			Thread[Thread-2,5,main]
//			Thread[Thread-1,5,X]
//			Thread[Thread-3,5,Y]
//			Threads in the X Thread Group
//			Thread[Thread-1,5,X]
//			Thread[Thread-3,5,Y]
		
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值