VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • 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
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.35.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.55.6

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

六、源码:

  • 所在包:com.ruigege.ThreadFoundation1
  • https://github.com/ruigege66/ConcurrentJava
  • CSDN:https://blog.csdn.net/weixin_44630050
  • 博客园:https://www.cnblogs.com/ruigege0000/
出  处:https://www.cnblogs.com/ruigege0000/p/13934755.html

相关教程