多线程

本文详细介绍了Java中的多线程概念,包括线程的优点、多任务处理的两种方式、Thread类与Runnable接口的使用、线程生命周期以及相关方法。此外,还探讨了线程同步工具如synchronized、wait和notify的使用,以及高级多线程控制类如ThreadLocal、原子类和Lock。最后,提到了阻塞队列BlockQueue和线程安全的ConcurrentHashMap。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java中的多线程是一个同时执行多个线程的进程,线程是一个轻量级的子进程,是最小的处理单位。线程是进程的一条执行路径,

而进程是线程的集合。

1.优点:

         ① 他不会阻塞用户,因为线程是独立的,可以同时执行多个操作。

         ② 可以一起执行许多操作,因此可以节省时间。(提高程序效率)

         ③ 线程是独立的,因此如果单个线程中发生异常,他不会影响其他线程。

 

2.多任务处理

         多任务处理是同时执行多个任务的过程。使用多任务来利用CPU,多任务处理可以通过两种方式来实现:

         ① 基于进程的多任务处理(多进程)

              特点:每个进程在内存中都单独分配有一个内存区域,进程是重量级的。进程之间的通信成本很高。从

                        一个进程切换到另一个进程需要一些时间来保存和加载寄存器、内存映射、更新列表等。

         ② 基于线程的多任务处理 (多线程)

              特点:线程共享相同的地址空间;线程之间是轻量级的;线程之间的通信成本很低。

3.Thread类

   java提供了Thread类来实现线程编程。Thread类提供了在线程上创建和执行操作的构造函数和方法。Thread类

   扩展了object类并实现了runnable接口。

   创建线程的方式: MyThread myThread = new MyThread();

4.runnable接口

   创建线程有两种方式,一种是直接继承Thread类,另外一种是实现runnable接口。相比较Thread而言,通过实现

   runnable的方式可以更容易地实现资源共享,并且接口可以多实现且还能再继承其他类。

  总结:① 适合多个相同的程序代码的线程去处理同一个资源

             ② 可以java中单继承的限制

             ③ 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

  创建线程的方式: MyThread myThread = new MyThread();     //只需要创建一个实例化对象然后被多个线程使用

                               Thread Thread1 = new Thread(myThread);  //线程1

                               Thread Thread2 = new Thread(myThread);  //线程2

 

  5.线程生命周期

            ① 新建状态:通过new创建一个线程,跟其他对象的创建时一样的。此时虚拟机会为其分配内存,并初始化

                                   其成员变量的值。

            ② 就绪状态:当线程对象调用了start方法之后,线程就进入了就绪状态。程序就会为其创建方法调用栈和程

                                   序计数器,处于这个状态中的线程并没有开始运行,只是表示该线程可以运行了,至于何时运

                                   行,取决于jvm中的线程调度器的调度。

            ③ 运行状态:当处于就绪状态的线程获得cpu,开始执行run()方法的线程执行体时,线程就进入了运行状态,

                                   线程调度的细节取决于底层平台采用的策略(抢占式调度策略和协作式调度策略)

            ④ 阻塞状态:运行状态的线程遇到以下情况就会进入阻塞状态:

                                  1.线程自己调用sleep方法主动放弃处理器资源;

                                  2.该线程调用了一个阻塞式IO方法,在该方法返回之前,该线程处于阻塞状态。

                                  3.该线程试图获得一个同步监视器,但该同步监视器正被其他线程持有。

                                  4.线程正在等待某个通知(notify)

                                  5.程序调用了线程的suspend方法,将线程挂起(容易导致死锁,避免使用)

           ⑤ 死亡状态:有三种情况会使线程进入死亡状态:

                                  1.run方法执行完成,线程正常结束。

                                  2.线程抛出未被捕获的exception或者error

                                  3.人为停止,比如调用stop方法(暴力停止,不建议使用),以及其他停止策略。

       

   6.线程中常用的方法

package com.multithreading.test;

//创建线程类,方法一
public class MyThread extends Thread {
	public MyThread() {
		
	}
	public MyThread(String threadName,ThreadGroup tg) {
		super(tg,threadName);
		start();
	}
	//线程功能函数
	public void run() {
		for(int i=1;i<=5;i++) {
			try {
				Thread.sleep(500);
			} catch (Exception e) {
				System.out.println(e.toString());
			}
				System.out.println(Thread.currentThread().getName()+":"+i);
			
		}
		
	}
	
	
    public static void main(String[] args) throws InterruptedException {
    	
    	//创建线程的方式1
    	MyThread myThread = new MyThread();
    	myThread.start();
    	myThread.interruptAndIsInterruptedAndInterrupted();
    	
    	
    	
    	//创建线程的方式2
    	MyThread3 MyThread3 = new MyThread3();
    	
    	//线程1
    	Thread t1 = new Thread(MyThread3);
    		   t1.start();
        //线程2
        Thread t2 = new Thread(MyThread3);
    		   t2.start();
        
 
    	
    	
	}
    
   	//1. start()  启动线程  线程方法
    public static void starts() {
    	MyThread myThread= new MyThread();
    	myThread.start();
    	
    }
    //2. run()  执行线程  线程方法
    public static void runs() {
    	
    	//开启线程之后,会自动运行run方法执行线程代码功能。如上上
    	MyThread myThread= new MyThread();
    	myThread.start();
    	
    }
    
    //3. sleep() 指定的时间内休眠线程
    public static void sleep() throws InterruptedException {
    	MyThread myThread= new MyThread();
    	myThread.start();
    	myThread.sleep(1000);   //休眠一秒钟(多线程中不会释放对象锁,与wait的比较)
    	
    }
    //4. currentThread()    返回对当前正在执行线程对象的引用   线程方法
    public static void currentThreads() {
    	MyThread myThread= new MyThread();
    	myThread.setName("我是MyThread");
    	myThread.start();
    	Thread t = Thread.currentThread();   //主线程
    	System.out.println(myThread.getName());
    	
    }
    //5. join()    实现线程的执行顺序  线程方法
    public static void joins() {
    	MyThread myThread1= new MyThread();
    	myThread1.setName("myThread1");
    	MyThread myThread2= new MyThread();
    	myThread2.setName("myThread2");
    	MyThread myThread3= new MyThread();
    	myThread3.setName("myThread3");
    	
    	myThread1.start();
    	try {
			myThread1.join(1500);   //myThread1优先执行1500毫秒
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	myThread2.start();
    	myThread3.start();
    	
    }
    
    //6. setpriority()  设置线程优先级(1-10,默认为5,数字越大优先级越高,但不代表级别越高就一定优先执行,只是优先执行的概率更大)  
    //7. getPriority()  返回线程优先级级别
    //另外还有三个常规优先级属性   MIN_PRIORITY设置最小优先级,值为1     MAX_PRIORITY设置最大优先级,值为10   NORM_PRIORITY普通优先级值为5   
    public static void setPriorityAndGetPriority() {
    	MyThread myThread1= new MyThread();
    	MyThread myThread2= new MyThread();
    	
    	myThread1.setPriority(1);
    	myThread1.setName("myThread1");
    	System.out.println(myThread1.getPriority());
    	
    	myThread2.setPriority(9);
    	myThread2.setName("myThread2");
    	System.out.println(myThread2.getPriority());
    	
    	myThread1.start();
    	myThread2.start();
    	
    	//myThread1.setPriority(Thread.MIN_PRIORITY);
    	//myThread1.setPriority(Thread.MAX_PRIORITY);
    	//myThread1.setPriority(Thread.NORM_PRIORITY);
    	
    }
    
    //8. setName() 设置线程名称
    //9. getName() 获取线程名称
    public static void setNameAndGetName() {
    	//以上方法中已经多次应用
    }

    //返回线程编号
    public static void geId() {
    	MyThread myThread= new MyThread();
    	myThread.getId();
    	System.out.println(myThread.getId());
    }
    
    
    //19. isAlive()  测试线程是否活着  线程方法
    public static void isAlives() {
    	MyThread myThread= new MyThread();
    	myThread.start();
    	System.out.println(myThread.isAlive());  
    }
    
    //11. yield()      Thread类的yield()方法导致当前正在执行的线程对象暂停并允许其他线程执行(其实就是降低优先级,但不代表不会先执行)
    public static void yield() {
    	MyThread myThread1= new MyThread();
    	myThread1.setName("myThread1");
    	MyThread myThread2= new MyThread();
    	myThread2.setName("myThread2");
    	myThread1.start();
    	myThread2.start();
    	
    	for(int i=0;i<3;i++) {
    		//如果myThread1线程正在执行,则执行该方法,意味着自己不运行run,重新回到就绪状态与其他线程一同竞争cpu时间
    		myThread1.yield();
    		System.out.println(myThread1.getName()+":我们一同竞争吧");
    	}
         
    }
    
    //12. stop()   暴力停止线程,强制中断线程的执行。容易造成数据不一致,属于不安全的方法,包括suspend()(阻塞线程)  resume()(恢复线程) 等已经弃用
    public static void  stops() {
    	
    	//略
    	
    	
    }
    //13. destroy()    销毁线程组以及子线程组
    //ThreadGroup  线程组类,管理线程的类,即线程组就是由线程组成的管理线程的类,这个类是java.lang.ThreadGroup类
    public static void destroys() throws InterruptedException {
    	//创建一个名为Parent thread的线程组
    	ThreadGroup g1 = new ThreadGroup("Parent thread");  
    	
    	//创建一个名为Child thread的线程组
    	ThreadGroup g2 = new ThreadGroup("Child thread");  
    	
    	//在线程组中创建线程,在有参构造函数已经启动了线程
    	MyThread myThread1= new MyThread("myThread1",g1);
    	MyThread myThread2= new MyThread("myThread2",g2);
    	
    	
    	myThread1.join();
    	myThread2.join();
    	
    	g1.destroy();
    	System.out.println(g1.getName()+" is destoried");
    	
    	g2.destroy();
    	System.out.println(g2.getName()+" is destoried");
    }
    
    // 	14. isDaemon()  该线程是否是一个守护线程
    //  15. setDaemon() 将线程标记为守护线程或用户线程
    public static void  isDaemons() {
       	MyThread myThread= new MyThread();
    	
       	System.out.println(myThread.isDaemon());
       	
       	myThread.setDaemon(true);
       	
       	System.out.println(myThread.isDaemon());
       	//守护线程与用户线程(非守护线程)
       	//守护线程是为用户线程服务的,比如垃圾回收线程,非必要线程。当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程
       	//用户线程就是执行业务功能代码的线程。
       	
       	
    }
    
    //16. interrupt()      标记需要中断的线程,中断的执行由具体实现代码决定
    //17. isInterrupted()  用来判断当前线程的中断状态
    //18. interrupted()    此方法清除线程的中断状态,这意味着如果要连续两次调用此方法,则第二次调用将返回false。 如果线程的中断状态为true,则此方法将状态设置为false
    public  void interruptAndIsInterruptedAndInterrupted() {
    	MyThread2 myThread= new MyThread2();
      	
      	myThread.start();
      	
      	System.out.println(myThread.isAlive());
      	
      	myThread.interrupt();
    	System.out.println(myThread.isAlive());	
      	
    }
   
    
    class MyThread2 extends Thread{
    		
    	public void run() {
    		for(int i=1;i<=5;i++) {
    			try {
    				//为true时,说明被标记为需要中断的线程,然后通过return直接返回来实现中断线程
    			     if(Thread.currentThread().isInterrupted()) {
    			    	 return;
    			     }
    			} catch (Exception e) {
    				System.out.println(e.toString());
    			}
    				System.out.println(Thread.currentThread().getName()+":"+i);
    			
    		}
    		
    	}
    }
    
    
    //19. notify()    随机唤醒wait等待池中的线程,从而可以去竞争获取对象锁
    //20. notifyAll()  唤醒所有wait等待池中的线程
    public  static void notifyAndNotifyAll() {
    	
    	//略
    	
    }
    //21. wait  使线程进行阻塞状态,并且会释放锁
    public  static void waits() {
    	
    	//略
    	
    }
    
}


//创建线程方法2,实现rnnable接口
class MyThread3 implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
	}
	
}

7.monitor(监视器)

    sychronized、wait、notify是任何对象都具有的同步工具。

   

    他们是应用于同步问题的人工线程调度工具。

    java中的每一个对象都有一个监视器来检测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在

    sychronized范围内,监视器发挥作用。

    wait/notify必须存在于sychronized块中。并且这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着

    wait之后,其他线程就可以进入同步块执行。

    当某代码并不持有监视器的使用权时(如上图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMo      
    nitorStateException。也包括在sychronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会

    抛出此异常。

     用法:

      sychronized单独使用:

      ① 代码块:如下,在多线程环境下,sychronized块中的方法获取lock实例的monitor,如果实例相同,那么只有一个线程

                     可以执行该代码块。

public class Thread1 implements Runnable {
   Object lock;
   public void run() {  
       synchronized(lock){
         ..do something
       }
   }
}

      ② 直接用于方法:相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的

                                    是static方法,则锁定该类所有实例。

public class Thread1 implements Runnable {
   public synchronized void run() {  
        ..do something
   }
}

     sychronized、wait、notify结合:典型场景生产者消费者问题

     

/**
   * 生产者生产出来的产品交给店员
   */
  public synchronized void produce()
  {
      if(this.product >= MAX_PRODUCT)
      {
          try
          {
              wait();  
              System.out.println("产品已满,请稍候再生产");
          }
          catch(InterruptedException e)
          {
              e.printStackTrace();
          }
          return;
      }

      this.product++;
      System.out.println("生产者生产第" + this.product + "个产品.");
      notifyAll();   //通知等待区的消费者可以取出产品了
  }

  /**
   * 消费者从店员取产品
   */
  public synchronized void consume()
  {
      if(this.product <= MIN_PRODUCT)
      {
          try 
          {
              wait(); 
              System.out.println("缺货,稍候再取");
          } 
          catch (InterruptedException e) 
          {
              e.printStackTrace();
          }
          return;
      }

      System.out.println("消费者取走了第" + this.product + "个产品.");
      this.product--;
      notifyAll();   //通知等待去的生产者可以生产产品了
  }

   volatile: https://blog.youkuaiyun.com/weixin_41690908/article/details/86771819

8.高级多线程控制类  

         Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent,提供了大量高级工具,可以帮助开发者编写高效、

易维护、结构清晰的Java多线程程序。       

  ①ThreadLocal类   

      用处: 保存线程的独立变量。对一个线程类(继承自Thread)
                  当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线

                  程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session

                 信息。

      实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里

                放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。
      主要方法是get()和set(T a),set之后在map里维护一个threadLocal -> a,get时将a返回。ThreadLocal是一个特殊的容

      器。

  ②原子类(AtomicInteger、AtomicBoolean……)

      为八大基本数据类型配置了对应的原子类。。。

   ③lock类

      lock : 在java.util.concurrent包内。共有三个实现:

        ReentrantLock
        ReentrantReadWriteLock.ReadLock
        ReentrantReadWriteLock.WriteLock

               主要目的是和synchronized一样, 两者都是为了解决同步问题,处理资源争端而产生的技术。功能类似但有

               一些区别。      

        lock更灵活,可以自由定义多把锁的枷锁解锁顺序(synchronized要按照先加的后解顺序)
        提供多种加锁方案,lock 阻塞式, trylock 无阻塞式, lockInterruptily 可打断式, 还有trylock的带超时时间版本。
        本质上和监视器锁(即synchronized是一样的)
        能力越大,责任越大,必须控制好加锁和解锁,否则会导致灾难。
        和Condition类的结合。
        性能更高,对比如下图:

               

                                 synchronized和Lock性能对比

        ReentrantLock

        可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。
        使用方法是:

        先new一个实例       static ReentrantLock r = new ReentrantLock();

        加锁:r.lock()或r.lockInterruptibly();

                   两种加锁方式也是不同,后者可被打断。当a线程lock后,b线程阻塞,此时如果是lockinterruptibly,

                   那么在调用b.interrupt()之后,b线程退出阻塞,并放弃对资源的争夺,进入catch块(如果使用后者,

                   必须throw interruptable exception 或catch)  

        释放锁:r.unlock()

                       放在finally里面。以防止异常跳出了正常流程,导致灾难。这里补充一个小知识点,finally是可以

                       信任的:经过测试,哪怕是发生了OutofMemoryError,finally块中的语句执行也能够得到保证。

         ReentrantReadWriteLock  

         可重入读写锁(读写锁的一个实现)

    ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
            ReadLock r = lock.readLock();
            WriteLock w = lock.writeLock();

         两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码。

9.容器类

   这里就讨论比较常用的两个:BlockQueue和ConcurrentHashMap

    ① BlockQueue

         阻塞队列。该类是java.util.concurrent包下的重要类,BlockQueue是单向队列,可以在队列头部添加元素

         或者在队列尾部删除取出元素类似于一个管道,特别适用于先进先出策略的一些应用场景。普通的queue

         接口主要实现有PriorityQueue(优先队列)

         BlockingQueue在队列的基础上添加了多线程协作的功能:

        

         除了传统的queue功能(表格左边的两列)之外,还提供了阻塞接口put和take,带超时功能的阻塞接口offer

         和poll。put会在队列满的时候阻塞,直到有空间时被唤醒,take在队列空的时候阻塞,直到有东西可以被拿

         的时候才被唤醒。用于生产者-消费者模型尤其好用。

          常见的阻塞队列有:

           ArrayListBlockingQueue

           LinkedListBlockingQueue

           DelayQueue

           sychronizedBlockingqueue

      ② ConcurrentHashMap

           高效的线程安全哈希map。对比hashTable、concurrentHashMap、HashMap

10.管理类

   管理类的概念比较范,用于管理线程,本身不是多线程的,但提供了一些机制来利用上述的一些工具做一些封装。

   值得一提的管理类:ThreadPoolExecutor和JMX框架下的系统级管理类ThreadMXBean。

   ThreadPoolExecutor 

ExecutorService e = Executors.newCachedThreadPool();
    ExecutorService e = Executors.newSingleThreadExecutor();
    ExecutorService e = Executors.newFixedThreadPool(3);
    // 第一种是可变大小线程池,按照任务数来分配线程,
    // 第二种是单线程池,相当于FixedThreadPool(1)
    // 第三种是固定大小线程池。
    // 然后运行
    e.execute(new MyRunnableImpl());

  该类内部是通过ThreadPoolExecutor实现的,掌握该类有助于线程池的管理,本质上他们都是ThreadpoolExecutor

  类的各种实现版本。

以上各参数:

corePoolSize:池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。
maximumPoolSize:线程最大值,线程的增长始终不会超过该值。
keepAliveTime:当池内线程数高于corePoolSize时,经过多少时间多余的空闲线程才会被回收。回收前处于wait状态
unit:
时间单位,可以使用TimeUnit的实例,如TimeUnit.MILLISECONDS 
workQueue:待入任务(Runnable)的等待场所,该参数主要影响调度策略,如公平与否,是否产生饿死(starving)
threadFactory:线程工厂类,有默认实现,如果有自定义的需要则需要自己实现ThreadFactory接口并作为参数传入。

 

   

         

  

 

  

      

            

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值