Java多线程
1.进程和线程
1.1串行与并行
- 串行:多个任务按顺序执行,完成一个之后,才进行下一个;
- 并行:多个任务同时执行,异步是多个任务并行的前提条件;
1.2并行与并发
- 并行:同一时刻,多个命令在多个处理器上同时执行,是真正的同时;
- 并发:同一时刻,只有一条命令是在处理器上执行,单多个进程命令被快速轮换执行,使得宏观上具有多个进程同时执行的效果;
对于一个CPU而言,只能在某一时间点执行一个程序;
1.3进程与线程
- 进程:
- 概述:
- 通俗讲就是:正在进行的程序;通常一个任务就是一个程序,而一个程序就是一个进程;
- 进程是操作系统控制中最基本的运行单元;
- 特点:
- 独立性:进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的地址空间,没有进程本身的运行,用户进程不可以直接访问其他进程的地址空间;
- 动态性:进程和程序的区别在于进程是动态的,进程中有时间的概念,进程具有自己的生命周期和各种不同的状态;
- 并发性:多个进程可以在单个处理器上并发执行,互不影响;
- 概述:
- 线程:
- 当程序进入内存时,即成为线程;
- 进程中的一个执行任务就是线程;
- 一个进程可以运行多个线程,多个线程可以共享数据;
1.4 线程状态
- Java中的线程的生命周期大体可分为5种状态;
- new状态(初始):创建一个线程对象,但还没有调用start()方法;
- 实现Runnable接口;
- 继承Thread;
- 可运行状态:线程对象创建之后,其他线程调用了该对象的使用方法,该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权;
- 运行状态:可运行状态的线程获得了cpu时间片,执行程序代码;
- 就绪状态(ready):start之后直接先来就绪状态;
- 运行中状态(running):
- 阻塞状态:线程因为某种愿意放弃了cpu的使用权,暂停运行;直到线程进入可运行状态,才有机会转到运行态;
- 等待阻塞:执行**.wait()**方法,JVM将该线程放入等待队列;
- 同步阻塞:线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会将该线程放入锁池中;
- 终止状态:表示该线程已经执行完毕;终止的线程不可再生;
- new状态(初始):创建一个线程对象,但还没有调用start()方法;
2.线程对象
2.1 线程定义:
-
实现Runnable接口
-
提供一个Runnable对象,该Runnable接口定义了一个方法,run用于包含在线程中执行的代码;
-
该Runnable对象将传递给构造函数;
/* 实现Runnable接口:灵活性强; Thread.currentThread().getName() //获得线程名称 */ public class HelloRunnable implements Runnable { /* 1.实现接口; 2.重写run()方法; 3.实例化对象:自己写的对象和官方的; 4.调用start()方法; */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("子线程名称:" + Thread.currentThread().getName() + "我是子线程" + i); } } //主方法/主线程 public static void main(String[] args) { //实例化子线程 //方法1: HelloRunnable hR = new HelloRunnable(); Thread thread = new Thread(hR); thread.start(); //导致此线程开始执行 //方法2: /* (new Thread(new HelloRunnable())).start(); */ //方法3: /* new Thread(new HelloRunnable()).start(); */ for (int i = 0; i < 10; i++) { System.out.println("主线程名称:" + Thread.currentThread().getName() + "我是主线程main" + i); } } }
-
-
继承Thread类
-
在Thread类自身实现Runnable,但其run()方法不起作用;
-
继承Thread的子类需要重写run()方法;
/* 继承Thread类:代码少灵活 */ public class HelloThread extends Thread { /* 1.继承Thread类; 2.重写run()方法; 3.实例化类; 4.调用start()方法; */ /* 重写run()方法 */ public void run() { super.run(); for (int i = 0; i < 10; i++) { System.out.println("我是子线程" + i); } } public static void main(String[] args) { //实例化子线程 HelloThread hT = new HelloThread(); hT.start(); for (int i = 0; i < 10; i++) { System.out.println("我是主线程main" + i); } } }
-
2.2 线程命名:
-
原因:所有线程程序的执行,每一次都是不同的运行结果,如果想要区分每一个线程,那么就必须依靠线程的名字;
-
命名:
-
在调用start()方法之前;
-
命名方法:
-
Runnable接口命名:
方法1:构造函数 子线程在构造函数中,直接写入名字: Thread thread = new Thread(hR,"我是子线程son1:"); 主线程: Thread.currentThread().setName("我是主线程main1"); public class RunnableReName01 implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("子线程名称:" + Thread.currentThread().getName() + "我是子线程" + i); } } //主方法/主线程 public static void main(String[] args) { //实例化子线程 HelloRunnable hR = new HelloRunnable(); Thread thread = new Thread(hR,"我是子线程son1:");//更改子线程名 thread.start(); //导致此线程开始执行 Thread.currentThread().setName("我是主线程main1"); //更改主线程; for (int i = 0; i < 10; i++) { System.out.println("主线程名称:" + Thread.currentThread().getName() + "我是主线程main" + i); } } } 方法2:set方法 子线程在构造函数中,直接写入名字: thread.setName("我是子线程son1:");//更改子线程名 主线程: Thread.currentThread().setName("我是主线程main1"); public class RunnableReName02 implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("子线程名称:" + Thread.currentThread().getName() + "我是子线程" + i); } } //主方法/主线程 public static void main(String[] args) { //实例化子线程 HelloRunnable hR = new HelloRunnable(); Thread thread = new Thread(hR); thread.setName("我是子线程son1:");//更改子线程名 thread.start(); //导致此线程开始执行 Thread.currentThread().setName("我是主线程main1"); //更改主线程; for (int i = 0; i < 10; i++) { System.out.println("主线程名称:" + Thread.currentThread().getName() + "我是主线程main" + i); } } }
-
Thread类命名:
方法1:构造函数 1.需要先生成一个名字方法 public ThreadReName01(String name) { super(name); } 2.子线程在构造函数中,直接写入名字: ThreadReName01 rT1 = new ThreadReName01("我是子线程:"); 主线程: Thread.currentThread().setName("我是主线程main1"); public class ThreadReName01 extends Thread { public ThreadReName01(String name) { super(name); } /* 重写run()方法 */ public void run() { super.run(); for (int i = 0; i < 10; i++) { System.out.println("子线程名称:" + Thread.currentThread().getName() + "我是子线程" + i); } } public static void main(String[] args) { //实例化子线程 ThreadReName01 rT1 = new ThreadReName01("我是子线程:"); rT1.start(); Thread.currentThread().setName("我是主线程main1"); for (int i = 0; i < 10; i++) { System.out.println("主线程名称:" + Thread.currentThread().getName() + "我是主线程main" + i); } } } 方法2:set方法 子线程:在构造函数中,直接写入名字: rT2.setName("我是子线程1"); 主线程: Thread.currentThread().setName("我是主线程main1"); public class ThreadReName02 extends Thread{ /* 重写run()方法 */ public void run() { super.run(); for (int i = 0; i < 10; i++) { System.out.println("子线程名称:" + Thread.currentThread().getName() + "我是子线程" + i); } } public static void main(String[] args) { //实例化子线程 ThreadReName02 rT2 = new ThreadReName02(); rT2.setName("我是子线程1"); rT2.start(); Thread.currentThread().setName("我是主线程main1"); for (int i = 0; i < 10; i++) { System.out.println("主线程名称:" + Thread.currentThread().getName() + "我是主线程main" + i); } } }
-
2.3 线程优先级
-
线程优先级的范围是1~10,默认的优先级是5;最高是10;
-
“高优先级线程”被分配CPU的概率高于“低优先级线程”;
public class ThreadPriority implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("线程名称:" + Thread.currentThread().getName() + "\t优先级" + Thread.currentThread().getPriority() +"\t循环次数:"+ i); } } public static void main(String[] args) { ThreadPriority tP = new ThreadPriority(); Thread thread1 = new Thread(tP,"子线程1:"); thread1.setPriority(Thread.MIN_PRIORITY); //设置优先级 需在start之前 thread1.start(); Thread thread2 = new Thread(tP,"子线程2:"); thread2.setPriority(Thread.MAX_PRIORITY); //设置优先级 thread2.start(); } }
2.4 线程休眠:
-
将当前线程在指定时间段内暂停执行,用于其他线程或者正在运行的其他程序;
-
调用方法:sleep(long millis) ;
public class TreadSleep implements Runnable{ @Override public void run() { String info[] ={ "人生若只如初见", "何事秋风悲画扇", "等闲变却故人心", "却道故人心易边" }; for (int i = 0; i < info.length; i++) { System.out.println(info[i]); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { TreadSleep ts = new TreadSleep(); Thread thread = new Thread(ts); thread.start(); } }
2.5 线程礼让:
-
给当前正在运行的线程一个提醒,告诉它可以将资源礼让给其他线程,但这只是一种暗示,没有任何一种机制保证当前线程会将资源礼让;
-
调用方法:yield();
-
yield()方法使具有同样优先级的线程有进入可执行状态的机会;
-
当当前线程放弃执行权的时候会再度回到就绪状态;
public class ThreadYield implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始执行"); Thread.yield(); //线程礼让 System.out.println(Thread.currentThread().getName() + "结束"); } public static void main(String[] args) { ThreadYield tY = new ThreadYield(); Thread thread1 = new Thread(tY,"线程1"); Thread thread2 = new Thread(tY,"线程2"); thread1.start(); thread2.start(); } }
2.6 线程联合:
-
允许一个线程等待另一个线程的完成;如果t是Thread正在执行其线程的对象;
-
t.join();
-
导致当前线程暂停执行,直到t的线程执行终止;
-
简单理解就是插队;
-
调用方法:join();
public class ThreadJoin implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("线程名称:" + Thread.currentThread().getName() + "我是子线程:" + i); } } //主线程 public static void main(String[] args) { //子线程实例化 ThreadJoin tJ = new ThreadJoin(); //改名 Thread thread = new Thread(tJ, "线程1:"); //启动 thread.start(); //该主线程名称 Thread.currentThread().setName("主线程main1"); for (int i = 0; i < 100; i++) { if (i == 50) { try { thread.join(); //线程加入,插队的意思 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("我是主线程:" + Thread.currentThread().getName() + ":" + i); } } }
2.7 线程停止
-
停止意味着放弃当前正在进行的操作;
-
Java中有3种方法啊可以终止正在运行的线程:
- 使用退出标识,使得线程正常退出,即当run方法完成后终止进程;
- 使用interrupt方法中断线程(不推荐使用);
- 使用stop方法强行中断线程(已过期);
public class ThreadStop implements Runnable { //标识 private static boolean flag ; @Override public void run() { int i = 0; while (flag) { System.out.println("我是子线程:" + i++); } } //主方法/主线程: public static void main(String[] args) { ThreadStop tS = new ThreadStop(); Thread thread = new Thread(tS); thread.start(); for (int i = 0; i < 100; i++) { System.out.println("我是主线程:" + i); if (i == 50) { ThreadStop.flag = false; } } } }
2.8 守护线程
-
Java线程分为两种:
- 用户线程:系统的工作线程,它会完成这个程序要完成的业务员操作;
- 守护线程:一种特殊的线程,是系统的守护者,在后台完成一些系统性的服务;
-
当用户线程全部结束,守护线程没有可以守护的对象也就结束;
public class ThreadDaemon { public static void main(String[] args) { //实例化守护线程 守护用户线程 Daemon daemon = new Daemon(); Thread thread1 = new Thread(daemon); thread1.setDaemon(true); thread1.start(); //实例化用户线程 User user = new User(); Thread thread2 = new Thread(user); thread2.start(); } } //用户线程 class User implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":我在努力奔跑" + i); } } } //守护线程 class Daemon implements Runnable { @Override public void run() { int i = 0; while (true) { System.out.println(Thread.currentThread().getName() + ":我在守护你" + i++); } } }
3.线程同步
3.1 线程冲突
-
当在不同线程中运行作用于相同数据的两个操作时,就会发生干扰,这意味着这两个操作由多个步骤组成,并且步骤顺序重叠;
- 检索的当前值c;
- 将检索到的值加1;
- 将增加的值存储回c;
public class ThreadConflict implements Runnable { /* * 问题1:输出语句顺序不对; * */ //票数100张 private static int ticket = 100; @Override public void run() { while (ticket > 0) { //会出现负值,冲突严重 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + --ticket + "张票"); } } public static void main(String[] args) { //实例化子线程 ThreadConflict tC = new ThreadConflict(); //模拟三个售票员 Thread thread1 = new Thread(tC, "售票员1号"); Thread thread2 = new Thread(tC, "售票员2号"); Thread thread3 = new Thread(tC, "售票员3号"); //启动线程 thread1.start(); thread2.start(); thread3.start(); } }
3.2 同步语句
-
同步语句(synchronized statements):必须制定提供内部锁的对象;
使用同步语句解决线程冲突: synchronized (""){ System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + --ticket + "张票"); } public class SynchronizedStatement implements Runnable { //票数100张 private static int ticket = 100; @Override public void run() { while (ticket > 0) { //会出现负值,冲突严重 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket <= 0) { return; } //同步语句解决线程冲突 synchronized ("") { System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + --ticket + "张票"); } } } public static void main(String[] args) { //实例化子线程 SynchronizedStatement SS = new SynchronizedStatement(); //模拟三个售票员 Thread thread1 = new Thread(SS, "售票员1号"); Thread thread2 = new Thread(SS, "售票员2号"); Thread thread3 = new Thread(SS, "售票员3号"); //启动线程 thread1.start(); thread2.start(); thread3.start(); } }
-
同步方法(synchronized methods):提供了一种防止线程干扰和内存一致性错误的简单策略;使用的时候只需将synchronized关键字添加到其声明中;
使用同步方法解决线程冲突: public synchronized void sellTicket(){ } public class SynchronrizedMethod implements Runnable { //票数100张 private static int ticket = 100; @Override public void run() { while (ticket > 0) { //会出现负值,冲突严重 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //调用同步方法 sellTicket(); } } public synchronized void sellTicket() { //临界资源需要都放进去 //票售空 if (ticket <= 0) { return; } System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + --ticket + "张票"); } public static void main(String[] args) { //实例化子线程 SynchronrizedMethod SM = new SynchronrizedMethod(); //模拟三个售票员 Thread thread1 = new Thread(SM, "售票员1号"); Thread thread2 = new Thread(SM, "售票员2号"); Thread thread3 = new Thread(SM, "售票员3号"); //启动线程 thread1.start(); thread2.start(); thread3.start(); } }
4.线程死锁
-
死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进;
-
死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁;
-
通常来说,死锁的场景是在多线程中,指的是两个或多个线程之间,由于互相持有对方需要的锁,互相等待,而永久处于阻塞状态;
public class ThreadDeadLock { public static void main(String[] args) { //实例化线程1 DeadLockA dLA = new DeadLockA(); Thread thread1 = new Thread(dLA, "线程1"); //实例化线程2 DeadLockB dLB = new DeadLockB(); Thread thread2 = new Thread(dLB, "线程2"); //启动线程 thread1.start(); thread2.start(); } } //线程1 class DeadLockA implements Runnable { @Override public void run() { synchronized ("A") { System.out.println("线程1获得了A锁"); synchronized ("B") { System.out.println("线程1获得了A锁和B锁"); } } } } //线程2 class DeadLockB implements Runnable { @Override public void run() { synchronized ("B") { System.out.println("线程2获得了B锁"); synchronized ("A") { System.out.println("线程2获得了B锁和A锁"); } } } }
5.线程协调
-
发生死锁的条件
- 互斥条件:一个资源每次只能被一个进程使用;
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺;
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系;
-
调用方法:
- wait()方法;
- notify()方法;
线程协调 解锁 方法1:wait() //等待 用来解锁 try { "A".wait(); } catch (InterruptedException e) { e.printStackTrace(); } 方法2:notify() "A".notify(); public class ThreadCoordinate { public static void main(String[] args) { //实例化线程1 DeadLock1 dLA = new DeadLock1(); Thread thread1 = new Thread(dLA, "线程1"); //实例化线程2 DeadLock2 dLB = new DeadLock2(); Thread thread2 = new Thread(dLB, "线程2"); //启动线程 thread1.start(); thread2.start(); } } //线程1 class DeadLock1 implements Runnable { @Override public void run() { synchronized ("A") { System.out.println("线程1获得了A锁"); //等待 用来解锁 try { "A".wait(); } catch (InterruptedException e) { e.printStackTrace(); } synchronized ("B") { System.out.println("线程1获得了A锁和B锁"); } } } } //线程2 class DeadLock2 implements Runnable { @Override public void run() { synchronized ("B") { System.out.println("线程2获得了B锁"); synchronized ("A") { System.out.println("线程2获得了B锁和A锁"); //解锁 "A".notify(); } } } }
-
单例:
- 单例指的是单个实例。如果一个类只能创建一个对象,这样的类就做单例类,这个对象就是单例;
- 特点:
- 只能有一个实例;
- 必须创建自己的唯一实例;
- 必须给所有其他对象提供这一实例;
- 将一个类定义为单例类:
- 提供一个获取本类对象的静态方法,方法内部要保证对象的唯一性;
-
懒汉式单例模式:
-
在首次使用时才加载;
-
优点:节省内存空间,首次用到的时候才创建,如果一直用不到,就一直不创建;
-
线程不安全,需要加上同步锁,影响了程序的执行效率;
public class ThreadSingleton { public static void main(String[] args) { for (int i = 0; i < 100; i++) { //实例化线程 HelloRunnable helloRunnable = new HelloRunnable(); Thread thread = new Thread(helloRunnable, "线程名称:" + i); //启动线程 thread.start(); } } } /* 单例 */ class Lazybones { public Lazybones() { System.out.println("对象实例化了"); } private static Lazybones lazybones = null; public static Lazybones getLazybones() { synchronized ("") { if (lazybones == null) { lazybones = new Lazybones(); } } return lazybones; } } /* 多线程 */ class HelloRunnable implements Runnable { @Override public void run() { Lazybones.getLazybones(); } }
-
-
饿汉式单例模式:
- 在加载这个类的时候,就先创建好了一个对象实例,等待调用;
- 天生线程安全,类加载的时候初始化一次对象,效率比懒汉式高;
- 正因为单例还没有使用的时候就已经对其初始化了,也就是说,如果整个程序从头到脚都没有用到这个单例,但是这个单例还是会创建对象,这就造成了不必要的资源浪费;
-
生产者消费者例子:
- 多线程环境下,我们经常需要多个线程的并发和协作。这个时候,就需要了解一个重要的多线程并发协作模型“生产者-消费者模式”;
- 生产者:指的是负责生产数据的模块(方法、对象、线程、进程);
- 消费者:指的是负责处理数据的模块(方法、对象、线程、进程);
- 缓冲区:消费者不能直接使用生产者的数据,它们之间有个”缓冲区“;生产者将数据生产好放入”缓冲区“,消费者从”缓冲区“拿到要处理的数据;
public class MainMethod { /* 1、产品/食物 2、缓冲区/柜台的大小 3、生产者/厨师 4、消费者/顾客 */ //主方法 public static void main(String[] args) { // 实例化缓冲区/柜台 ProductPool productPool = new ProductPool(8, new LinkedList<Product>()); // 实例化生产者/厨师 Producer producer = new Producer(productPool); Thread thread1 = new Thread(producer); // 实例化消费者/顾客 Consumer consumer = new Consumer(productPool); Thread thread2 = new Thread(consumer); //启动线程 thread1.start(); thread2.start(); } } /* 产品 */ public class Product { public String name; public Product(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } /* 缓冲区 */ import java.util.List; public class ProductPool { // 缓冲区大小 public int maxSize = 0; // 产品的列表 public List<Product> list; public ProductPool(int maxSize, List<Product> list) { this.maxSize = maxSize; this.list = list; } // 生产者生产产品,放入产品到缓冲区。 public synchronized void push(Product product) { // 产品的数量不能大于缓冲区的大小 // 如果大于缓冲区,就等待消费者消费。 if(this.list.size() >= this.maxSize) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 生产者生产了一个产品 this.list.add(product); // 通知消费者消费。 this.notifyAll(); } // 消费者消费产品,从缓冲区移除产品。 public synchronized Product pop() { // 产品数量不能小于0 // 如果小于0,就等待生产者生产产品。 if (this.list.size() <= 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 消费者消费了一个产品 Product product = this.list.remove(0); // 通知生产者生产产品。 this.notifyAll(); return product; } } /* 生产者 */ import java.security.PublicKey; public class Producer implements Runnable { // 缓冲区/柜台的大小 public ProductPool productPool; public Producer(ProductPool productPool) { this.productPool = productPool; } // 模拟生产者生产产品 @Override public void run() { int i = 0; while (true) { Product product = new Product("产品名称" + i++); this.productPool.push(product); System.out.println("生产者生产了产品" + product.getName()); } } } /* 消费者 */ public class Consumer implements Runnable { //缓冲区/柜台的大小 public ProductPool productPool; public Consumer(ProductPool productPool) { this.productPool = productPool; } //模拟消费者消费产品 @Override public void run() { while (true) { Product product = this.productPool.pop(); System.out.println("消费者消费了产品" + product.getName()); } } }
6.高级并发对象
6.1 线程定义 :实现Callable接口
-
解决Runnable接口和Thread类存在的以下问题:
- 无法获取子线程的返回值;
- run方法不可以抛出异常;
/** * 1、实现Callable接口 * 2、重写call方法 * 3、实例化类 * 4、调用start() * 5、获取返回值 */ import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class HelloCallable implements Callable { // 重写call方法 @Override public Object call() throws Exception { int j = 0; for (int i = 0; i < 10; i++) { System.out.println("我是子线程" + i); j++; } return j; } //主方法 public static void main(String[] args) { // 实例化类 HelloCallable helloCallable = new HelloCallable(); FutureTask futureTask = new FutureTask(helloCallable); Thread thread = new Thread(futureTask); thread.start(); try { // 获取返回值 System.out.println("返回值:" + futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
6.2 线程同步: 锁对象
- 同步代码依赖于一种简答的可重入锁。这种锁易于使用,但有很多限制;
- java.util.concurrent.locks软件包支持更复杂的锁定习惯用法;
- Lock对象的工作方式非常类似于同步代码所使用的的隐式锁;
- 与隐式锁一样,一次只能有一个线程拥有Lock对象;
- Lock对象和wait/notify通过其关联的Condition对象支持一种机制;
6.3 线程池:
- Java中创建和销毁一个线程是比较昂贵的操作,需要系统调用。频繁创建和销毁线程会影响系统的性能;
- 使用线程池的优点:
- 降低资源的消耗:线程本身是一种资源,创建和销毁线程会有CPU开销;创建的线程也会占用一定的内存;
- 提高任务执行的响应速度:任务执行时,可以不必等到线程创建完之后在执行;
- 提高线程的可管理性:线程不能无限制地创建,需要进行统一的分配、调优和监控;
6.4 并发集合:BlockingQueue
- BlockingQueue实现被设计主要为用于生产者-消费者队列;
- 如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒;
- 如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒;
- BlockingQueue实现的线程是安全的;
- 所有排队方法都可以使用内部锁或其他形式的并发控制原来原子地实现其效果;
/*
1.产品:
2.生产者:
3.消费者:
*/
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class test5_MainMethod {
public static void main(String[] args) {
//实例化缓冲区
BlockingQueue<test5_Product> pool = new ArrayBlockingQueue<>(12);
//实例化生产者:
test5_Producer producer = new test5_Producer(pool);
Thread thread1 = new Thread(producer);
//实例化消费者:
test5_Consumer consumer = new test5_Consumer(pool);
Thread thread2 = new Thread(consumer);
//启动线程:
thread1.start();
thread2.start();
}
}
/*
产品:
产品名
*/
public class test5_Product {
private String name;
public test5_Product(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/*
生产者:
需要缓冲区:系统提供的:BlockingQueue<>
生产产品:
1.创建产品对象:生成一个产品
2.每隔100ms生产一个产品
3.模拟生产产品
*/
import java.util.concurrent.BlockingQueue;
public class test5_Producer implements Runnable {
//缓冲区
public BlockingQueue<test5_Product> pool;
public test5_Producer(BlockingQueue<test5_Product> pool) {
this.pool = pool;
}
//生产产品
@Override
public void run() {
int i = 0;
while (true) {
//创建产品对象:生成一个产品
test5_Product product = new test5_Product("产品名称:" + i++);
//每隔100ms生产一个产品
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//模拟生产产品
try {
this.pool.put(product);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产了产品:" + product.getName());
}
}
}
/*
消费者:
需要缓冲区:系统提供的:BlockingQueue<>
消费产品:
1.获取产品
2.模拟消费产品
*/
import java.util.concurrent.BlockingQueue;
public class test5_Consumer implements Runnable {
public BlockingQueue<test5_Product> pool;
public test5_Consumer(BlockingQueue<test5_Product> pool) {
this.pool = pool;
}
//消费产品
@Override
public void run() {
while (true) {
//产品变量
test5_Product product = null;
//1000ms获取一个产品
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//模拟消费产品
try {
product = this.pool.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者消费了产品:" + product.getName());
}
}
}