Java高并发5-守护线程、ThreadLocal和死锁四个必要条件

一、复习

  • void interrupt()、boolean isInterrupted()、boolean interrupted()
  • interrupt()方法只是为了做一个“中断”的标记,而不会真的停止该线程、isInterrupted是返回线程的中断状态、interrupted()方法也是一样的,但是这是一个static方法,可以直接调用而且根据源码,是所在的线程的中断状态,而且如果结果为true,它会立即消除“中断”状态。

二、上下文切换

  • 定义:CPU会给各个线程分配时间片,当一个线程的时间片使用结束后,它会处于就绪状态,此时CPU会切换到另外一个线程,这就是上下文切换。
  • 上下文切换的时机有两个:(1)线程使用完时间片完全处于就绪状态时;(2)线程被中断;

三、死锁

  • 定义:两个或者两个以上的线程,当他们进行争夺资源用于执行的时候,形成一种互相等待的局面,就是死锁。

1.死锁产生的四个必要条件:

  • (1)互斥条件:线程对已经获得的资源进行排他性使用,也就是资源被占用,只能由一个线程占用,如果其他线程想要获得该资源,就必须等待,除非该线程释放了资源。
  • (2)请求并持有条件:一个线程占有了资源,并且还想要其他资源,但是此时其他资源被其他线程占用了,那么此时在等待资源释放,并且并不会释放自己已经持有的资源
  • (3)不可剥夺条件:一个线程占用了资源,直到它使用完才会释放,在此期间如果由其他线程想要该资源,是不能抢占的
  • (4)环路等待条件,也就是有一个线程集合{t1,t2,t3,…tn},t1想要t2的资源,t2想要t3的资源…直到tn想要t1的资源,形成一个环路
  • 举个死锁的例子
package com.ruigege.threadFoundation1;

public class DeadLockExample {
	//创建资源
	private static Object object1 = new Object();
	private static Object object2 = new Object();

	public static void main(String[] args) throws InterruptedException {
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized(object1) {
					System.out.println("线程1获得了资源1");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized(object2) {
						System.out.println("线程1获得了资源2");
					}
					
				}

			}
		});
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized(object2) {
					System.out.println("线程2获得了资源2");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized(object1) {
						System.out.println("线程2获得了资源1");
					}					
				}

			}
		});
		thread1.start();
		thread2.start();
		thread1.join();
		thread2.join();
	}
}

5.1

  • 两者互相等待上了,死锁的四个条件都满足了,形成了死锁

2.如何避免死锁

  • 至少需要破坏一个必要条件,学过操作系统的小伙伴知道,目前只用请求并持有条件和环路等待条件是可以破坏的。上面的代码稍微改动,把第二个线程的object1和object2,位置互换,也就是和线程1的顺序一致。运行:
    5.2

四、守护线程

  • 在Java中有两种线程一种是用户线程,一种是守护线程。我们使用的main函数就是一个用户线程,垃圾回收器就是一个守护线程。守护线程的描述就是JVM的退出并不取决于守护线程是否还在运行,也就是说,当用户线程结束的时候,退出JVM,此时是否有守护线程都无所谓,依然会退出。

1.创建一个守护线程

线程.setDaemon(true);
线程.start();
  • 调用setDaemon方法即可。我们来测试一下,上面描述的特性是否成立
package com.ruigege.threadFoundation1;

public class DaemonThreadTest {

	public static void main(String[] args) {
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				while(true) {
					//我们这里搞一个死循环也就是让子线程不停
				}
			}
		});
		
		thread1.setDaemon(true);
		thread1.start();
		
		System.out.println("主线程已经结束了,看一看后面还有没有光标在动");
		
	}
}

5.3
5.4

  • 第一个没设置为守护线程,可以看到虽然主线程已经结束了,但是子线程没有结束,程序仍然在运行;第二个就是设置了,但是主线程一结束,守护线程也结束了。

linux命令 ps -ef | grep java
这个Linux命令代表什么
ps代表的是显示某个进程;grep全称为global regular expression print代表是查找的意思,它的后面可以接正则表达式,这个grep java就是代表查找出所有带有java的进程然后显示出来;
|是管道符合,这里代表的意思就是前后两个命令同时执行;
ps后面跟一些参数,-e代表所有的进程;-f代表全称线程。还有其他参数,百度了一下。-h : 不显示标题;-l : 长格式-w : 宽输出;a :显示终端上的所有进程,包括其他用户的进程;r :只显示正在运行的进程;u :以用户为主的格式来显示程序状况;x :显示所有程序,不以终端机来区分。

  • 总结:如果你想要主线程结束之后马上退出JVM,那么就可以把子线程设置为守护线程。反之,就把子线程设置为用户线程。

五、ThreadLocal解析

  • 多线程访问同一个共享对象的时候,往往会有并发问题,那么我们可以通过加锁的方式来保证对象的内容统一,但是这种效率很低,于是Java的JDK提供了一个类ThreadLocal,该共享变量的工作原理是:一旦创建了一个共享变量之后,它会在每个线程中创建一个副本,用于本线程使用,实际操作的是自己线程独有的副本,保证线程安全,不会和其他线程混淆
  • 我们接下来举个例子:

package com.ruigege.threadFoundation1;

public class ThreadLocalTest {
	
	public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
	
	public static void printContentOfThreadLocal(ThreadLocal threadLocal) {
		System.out.println(threadLocal.get());
		threadLocal.remove();
		
	}
	
	public static void main(String[] args) {
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				threadLocal.set("共享变量1");
				System.out.println("线程1的共享变量是:");
				printContentOfThreadLocal(threadLocal);
				System.out.println("线程1去除共享变量后的值为:" + threadLocal.get());
			}
		});
		
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				threadLocal.set("共享变量2");
				System.out.println("线程2的共享变量是:");
				printContentOfThreadLocal(threadLocal);
				System.out.println("线程2去除共享变量后的值为:" + threadLocal.get());
			}
		});
		
		thread1.start();
		try {
			Thread.sleep(100);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		
		thread2.start();
		
	}
}

5.5
5.6

  • 第一个是把语句threadLocal.remove注释掉的结果,第二个是没有注释的结果。

六、源码:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值