java中的Thread

线程实现

线程实现的方法

  • extends Thread覆盖run()方法

  • 实现Runnable接口,实现run()方法

  • 实现Callable接口,实现call()方法

  • 基于线程池的方式

    • extends Thread覆盖run()方法
       //extends Thread覆盖run()方法
       public class ThreadA {
          public static void main(String[] args){
          ThreadAA threadAA = new ThreadAA();
          threadAA.start();
          System.out.println("这是主线程");
          }
       }
       class ThreadAA extends Thread {
          @Override
          public void run() {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.fillInStackTrace();
            }
            System.out.println("这是线程A");
           }
        }
    
    • 实现Runnable接口,实现run()方法
    	//实现Runnable接口,实现run()方法
    	 public class ThreadMain2 {
    		public static void main(String[] args){
    	        ThreadBB threadb = new ThreadBB();
    	        new Thread(threadb).start();
    	        System.out.println("这是主线程");
    	        try{
    	            Thread.sleep(10000);
    	        }catch(InterruptedException e){
    	            e.printStackTrace();
    	        }
    	    }
    	}
    	class ThreadBB implements Runnable{
    	    public void run(){
    	        try{
    	            Thread.sleep(10000);
    	        }catch(InterruptedException e){
    	            e.printStackTrace();
    	        }
    	        System.out.println("这是线程B");
    	    }
    	}
    
  • 实现Callable接口,实现call()方法

        public class ThreadMain3 {
          public static void main(String[] args){
      	    ThreadCC threadC = new ThreadCC();
      	    FutureTask<String> feature = new FutureTask<String>(threadC);
      	    new Thread(feature).start();
      	    System.out.println("这是主线程: begin!");
      	    try{
      	        System.out.println("得到返回结果是:" + feature.get());
      	    }catch(InterruptedException e1){
      	        e1.printStackTrace();
      	    }catch(ExecutionException e2){
      	        e2.printStackTrace();
      	    }
          	System.out.println("这是主线程; end!");
      	}
      }
      
      class ThreadCC implements Callable<String> {
          public String call() throws Exception{
              try{
                  Thread.sleep(500);
              }catch(InterruptedException e){
                  e.printStackTrace();
              }
              System.out.println("这是线程C");
              return "Thread C";
          }
      }
    ```
    
  • 基于线程池的方式

        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10); 
      	  while(true) { 
      		  threadPool.execute(new Runnable() { 
      			  // 提交多个线程任务,并执行 
      			  @Override 
      			  public void run() { 
      			 	 System.out.println(Thread.currentThread().getName() + " is running .."); 
      				  try { 
      				  	Thread.sleep(3000); 
      				  } catch (InterruptedException e) { 
      				 	 e.printStackTrace(); 
      				  } 
      		 	 } 
      		  });
      	   } 
         }
    
    • Thread使用时的注意事项:
      • 开启一个新的线程时,要给该线程一个名字,方便跟踪线程,排查问题。
      • resume、stop、suspend等方法已经被废除,不建议使用。
      • main方法主线程结束,新开启的子线程不一定结束。

线程中断机制

调用Thread.stop()

  • 线程不安全,不建议使用

利用Thread.interrupt()方法和机制

  • Thread类提供三个中断方法:
    • public static boolean interrupted():测试当前线程是否已经中断。线程的中断状态由该方法清除。
    • public boolean isInterrupred():测试线程是否已经中断。线程的中断状态不受该方法影响。
    • public void interrupt():中断线程,没有返回结果。是唯一能将中断状态设置为True的方法。
    public class InterruptThread1 implements Runnable{
        public static void main(String[] args) throws Exception{
            Thread thread = new Thread(new InterruptThread1(),"InterruptThread1");
            System.out.println("Starting thread...");
            thread.start();
            Thread.sleep(3000);
            System.out.println("Interrupting thread...");
            thread.interrupt();
            System.out.println("线程是否中断:" + thread.isInterrupted());
            Thread.sleep(3000);
            System.out.println("Stopping application...");
        }
        @Override
        public void run() {
            boolean stop = false;
            while (! stop){
                System.out.println("my Thread is running...");
                long time = System.currentTimeMillis();
    
                while ((System.currentTimeMillis() - time < 1000)){
    
                }
            }
            System.out.println("My Thread exiting under request...");
        }
    }
    
    运行结果:
      Starting thread...
      my Thread is running...
      my Thread is running...
      my Thread is running...
      Interrupting thread...
      线程是否中断:true
      my Thread is running...
      my Thread is running...
      my Thread is running...
      Stopping application...
      my Thread is running...
      my Thread is running...
      my Thread is running...
      my Thread is running...
      my Thread is running...
      my Thread is running...
      my Thread is running...
      my Thread is running...
      Disconnected from the target VM, address: '127.0.0.1:52211', transport: 'socket'
      my Thread is running...
    
    程My thread永远没有结束,My thread没有处理该中断该请求,就像这个线程没有被中断一样。
    当在run方法中添加以下代码可以处理终止请求:
    	//Thread.currentThread()获取当前线程
       if (Thread.currentThread().isInterrupted()){
           break;
       }
    

线程的生命周期

线程的生命周期状态

  • 线程的生命周期6个状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMEED_WAIT、TERMINATED。
    • 新建(new):创建Thread类的实例(对象)时,该线程进入新建状态,新生状态的线程有自己的内存空间,但该线程并没有运行。此时线程不是活着的(not alive)。
    • 运行(Runnable):线程已经被启动,正在等待被分配给CPU时间片。线程正在运行,也可能是线程在就绪队列中等待运行。此时线程时活着的(alive)。
    • 阻塞(Blocked):线程在等待获得锁。比如线程尝试通过synchronized获得某个锁,但是该锁已经被其他线程占用了。这时线程就处于阻塞状态。此时线程仍然是或者的(alive)。
      • 等待阻塞(o.wait -> 等待队列):运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
      • 同步阻塞(lock -> 锁池):运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
      • 其他阻塞(sleep/join):运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
    • 等待(Waiting):线程在等待某种资源就绪。当线程通过调用以下方法可以处于等待状态:
      • Object.wait()
      • Thread.join()
      • LockSupport,park()
        notify()/notifyAll()方法可以解除等待。
    • 计时等待(Timed_Wait): 线程进入条件和等待类似,但他调用的是带有超时时间的方法。线程调用以下方法可以进入计时等待状态:
      • Thread.sleep()
      • Object.wait()
      • Thread.join()
      • LockSupport.parkNanos()
      • LockSupport.parkUntil()
    • 终结(Terminated):线程正常退出或异常退出后,就处于终结状态。也可以叫线程的死亡。
      • 正常运行结束
      • 使用退出标志退出线程:如使用一个变量来控制循环。
      • interrupt方法结束进程
      • stop方法终止线程==(线程不安全)==
        线程生命周期状态图如下图所示:
        在这里插入图片描述

守护线程

  • 守护线程是后台运行线程,进程结束,守护线程也会结束,不需要手动设置或通知其状态。
  • 守护线程通过调用线程对象setDaemon(true)将其设置为守护线程。该方法必须在启动线程前调用:
    public final void setDaemon(boolean on)
    
    • JVM的垃圾回收、内存管理等线程是守护线程
      示例:
      public class DaemonThread {
          public static void main(String[] args){
              Thread tA = new ThreadA();
              Thread tB = new ThreadB();
      
              tA.setDaemon(true);
      
              tB.start();
              tA.start();
              Thread mainThread = Thread.currentThread();
              System.out.println("线程A 是不是守护线程? " + tA.isDaemon());
              System.out.println("线程B 是不是守护线程? " + tB.isDaemon());
              System.out.println("主线程 是不是守护线程? " + mainThread.isDaemon());
          }
      }
      class ThreadA extends Thread{
          @Override
          public void run() {
              for (long i = 0; i < 10; i++){
                  System.out.println("后台线程A第" + i + "次执行");
                  try {
                      Thread.sleep(7);
                  }catch (InterruptedException e){
                      e.fillInStackTrace();
                  }
              }
          }
      }
      class ThreadB extends Thread{
          @Override
          public void run() {
              for (long i = 0; i < 5; i++){
                  System.out.println("线程B第" + i + "次执行");
                  try {
                      Thread.sleep(7);
                  }catch (InterruptedException e){
                      e.fillInStackTrace();
                  }
              }
          }
      }
      
      执行结果:
      线程A 是不是守护线程? true
      后台线程A第0次执行
      线程B第0次执行
      线程B 是不是守护线程? false
      主线程 是不是守护线程? false
      线程B第1次执行
      后台线程A第1次执行
      线程B第2次执行
      后台线程A第2次执行
      后台线程A第3次执行
      线程B第3次执行
      后台线程A第4次执行
      线程B第4次执行
      后台线程A第5次执行
      
      前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。
      • 注意:
        • 在使用后台线程时,JRE判断程序是否执行结束的标准是所有前台程序执行完毕,不管后台线程的状态。
        • 当进程中所有非守护线程已结束或退出时,即使仍有守护线程在运行,进程仍然结束

线程组ThreadGroup

  • 线程组是为了方便线程管理出现的,线程组存在的意义,首要原因是安全。
  • 一个ThreadGroup都可以包含一组子线程和一组子线程组,一个进程中线程组是以树形的方式存在,通常情况下根线程组是system线程组,system线程组下是main线程组,默认情况下第一级应用自己的而线程组是通过main线程组创建出来的,system线程组是所有线程最顶级的父线程组
  • java允许对一个线程组中的所有线程同时进行操作。
  • java的多线程组机制的作用是线程安全。线程组机制允许通过分组来区分不同安全特性的线程,对不同组的线程进行不同的处理,通过线程组的分层结果支持不对等安全措施的采用。
  • 线程组合线程池的区别:
    • 概念不同
    • 作用不同:线程组是为了方便线程的管理线程池是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销

当前线程副本ThreadLocal

ThreadLocal

  • ThreadLocal为每个使用该变量的线程提供独立的变量副本,每个线程都可以独立地改变自己的副本,而不影响其他线程对应的副本。
  • ThreadLocal类的接口
    • void set(T value):设置当前线程的线程局部变量的值。
    • public T get():返回当前线程所对应的线程局部变量。
    • public void remove():将当前线程局部变量的值删除,目的是减少内存的占用。当线程结束后,对应该线程的局部变量将自动被垃圾回收,显式调用remove()方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收速递
    • ThreadLocal类示例
public class ThreadLocalDemo {

    //1、通过匿名内部类覆盖ThreadLocal的initialValue()方法
    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
    public ThreadLocal<Integer> getThreadLocal(){
        return seqNum;
    }

    //2、获取下一个序列值
    public int getNextNum(){
        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }

    public static void main(String[] args){
        ThreadLocalDemo sn = new ThreadLocalDemo();

        //3、 3个线程共享sn,各自产生序列号
        TestClient t1 = new TestClient(sn);
        TestClient t2 = new TestClient(sn);
        TestClient t3 = new TestClient(sn);
        t1.start();
        t2.start();
        t3.start();
    }

    private static class TestClient extends Thread{
        private ThreadLocalDemo sn;
        public TestClient(ThreadLocalDemo sn){
            this.sn = sn;
        }

        @Override
        public void run() {
            for (int i = 0; i < 3; i++){
                System.out.println("thread[" +
                        Thread.currentThread().getName() +"] --> sn[" + sn.getNextNum() + "]");
            }
            //每个线程用完时要记得删除
            sn.getThreadLocal().remove();
        }
    }
}

运行结果:

thread[Thread-0] --> sn[1]
thread[Thread-2] --> sn[1]
thread[Thread-1] --> sn[1]
thread[Thread-2] --> sn[2]
thread[Thread-0] --> sn[2]
thread[Thread-0] --> sn[3]
thread[Thread-2] --> sn[3]
thread[Thread-1] --> sn[2]
thread[Thread-1] --> sn[3]

每个线程所产生的序号虽然都共享同一个seqNum实例,但线程之间没有发生相互干扰的情况,而是各自产生独立的序列号,因为通过ThreadLocal为每个线程提供了单独的副本

ThreadLocal实现原理

  • ThreadLocal的set()方法源码实现:通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到ThreadLocalMap对象中,如果获取的ThreadLocalMap为空,通过createMap创建。
  • ThreadLocalMap是 ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取,每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改
  • ThreadLocal类通过操作每个线程特有的ThreadLocalMap副本,实现变量访问在不同线程中的隔离
  • ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,值是设置的对象
    • 不同的ThreadLocal对象作为不同键,也可以在线程的ThreadLocalMap对象中设置不同的值,通过ThreadLocal对象,在多线程中共享一个值和多个值得区别。就像在HashMap对象中存储一个键值对和多个键值对的一样。
    • ThreadLocal在处理线程的局部变量时要比synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
    • ThreadLocalMap使用ThreadLocal的弱引用作为key值。弱引用一定程度上回收了无用对象,但前提是开发者手动清理掉ThreadLocal对象的强引用。只要线程一直不死,ThreadLocalMap的key-value一直在涨,会导致内存泄漏。解决方法:使用ThreadLocal,一般都声明在静态变量中,调用remove()方法。

线程异常处理

  • run()方法不允许throw exception,所有的异常必须在run方法内处理。
  • 在java多线程中,所有线程都不允许抛出未捕获的checked exception,各个线程需要自己把自己的checked exception处理掉。
  • 在java中,线程方法的异常都应该在线程代码边界内(run方法内)进行try catch并处理。
  • 线程中出爱心check exception,推荐做法是采用try/catch块处理,对于unchecked exception,比较合理的方式是注册一个实现UncaughtExceptionHandler接口的对象实例处理。
    • 实例:
    public class ExceptionHandlerThread {
    
       public static void main(String[] args){
           ExceptionHandlerThreadB task = new ExceptionHandlerThreadB();
           Thread thread = new Thread(task);
           thread.setUncaughtExceptionHandler(new ExceptionHandlerThreadA());
           thread.start();
       }
    }
    class ExceptionHandlerThreadA implements Thread.UncaughtExceptionHandler{
       @Override
       public void uncaughtException(Thread t, Throwable e) {
           System.out.printf("An exception has been captured");
           System.out.printf("Thread: %s\n", t.getId());
           System.out.printf("Exception: %s: %s\n", e.getClass().getName(), e.getMessage());
           System.out.printf("Stack trace: \n");
           e.printStackTrace(System.out);
           System.out.printf("Thread status: %s\n", t.getState());
       }
    }
    class ExceptionHandlerThreadB implements Runnable{
       @Override
       public void run() {
           int number = Integer.parseInt("TTT");
       }
    }
    
    运行结果:
      An exception has been capturedThread: 11
     Exception: java.lang.NumberFormatException: For input string: "TTT"
     Stack trace: 
     java.lang.NumberFormatException: For input string: "TTT"
     	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
     	at java.lang.Integer.parseInt(Integer.java:580)
     	at java.lang.Integer.parseInt(Integer.java:615)
     	at com.lym.multithread.interrupt.ExceptionHandlerThreadB.run(ExceptionHandlerThread.java:36)
     	at java.lang.Thread.run(Thread.java:748)
     Thread status: RUNNABLE
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值