线程机制
线程机制(基础版)
进程的概念:
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
通俗来说,进程就是运行中的程序;进程是程序的一次执行过程,或是正在运行的一个程序。
线程的概念:
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如[Windows 7](https://baike.baidu.com/item/Windows 7?fromModule=lemma_inlink)的线程,进行混合调度。
同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。
一个进程可以有很多线程,每条线程并行执行不同的任务。
在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行,编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。
单线程
同一时刻,只允许执行一个线程
多线程
同一时刻,可以执行多个线程
并发和并行
并发:同一时刻,多个任务交替执行。单核CPU实现的多任务就是并发。
并行:同一时刻,多个任务同时执行。多核CPU可以实现并行。
主要观察存在几个CPU在执行程序,单个CPU来回切换执行程序属于并发,多个CPU同时执行不同程序属于并行。
并发和并行可以同时存在
进程和线程的区别
-
线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。
-
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
-
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
-
包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
-
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
-
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
-
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
线程的使用
一、继承Thread类
代码展示:
package com.wfy.ReviewThread;
/**
* 通过Thread类创建线程
*
* 1.当一个类继承了Thread类,该类就可以当做线程使用
* 2.重写run方法,写上自己的业务代码
* 3.run Thread类,实现了Runnable接口的run方法
*/
public class Thread01 {
public static void main(String[] args) {
EnglishTest englishTest= new EnglishTest();
englishTest.start();
}
}
class EnglishTest extends Thread{
//重写run方法,编写自己的业务逻辑
@Override
public void run() {
int TimeNums=0;
while (true) {
System.out.println("这次四级一定过~");
TimeNums++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (TimeNums>=10){
break;
}
}
}
}
二、通过实现Runnable接口
代码展示:
package com.wfy.ReviewThread;
/*
通过实现Runnable接口来开发线程
*/
public class Runnable01 {
public static void main(String[] args) {
EnglishContest englishContest = new EnglishContest();
//通过Runnable接口创建线程不能直接调用start方法开启线程
Thread thread = new Thread(englishContest);
thread.start();
}
}
class EnglishContest implements Runnable{
@Override
public void run() {
int TimeNums=0;
while (true) {
System.out.println("这次四级一定必过~"+(TimeNums++));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (TimeNums>=10){
break;
}
}
}
}
三、多线程的开启
代码展示:
package com.wfy.ReviewThread;
public class Thread02 {
public static void main(String[] args) {
Thread_01 thread_01 = new Thread_01();
Thread_02 thread_02 = new Thread_02();
Thread thread1 = new Thread(thread_01);
Thread thread2 = new Thread(thread_02);
thread1.start();
thread2.start();
}
}
class Thread_01 implements Runnable{
@Override
public void run() {
int TimeNums=0;
while (true) {
System.out.println("线程一开启"+Thread.currentThread().getName());
TimeNums++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (TimeNums>=20){
break;
}
}
}
}
class Thread_02 implements Runnable{
@Override
public void run() {
int TimeNum=0;
while (true) {
System.out.println("线程二开启"+Thread.currentThread().getName());
TimeNum++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (TimeNum>=20){
break;
}
}
}
}
通过售票案例,解决多线程问题
一、通过继承Thread类
package com.wfy.ReviewThread;
public class Thread03 {
public static void main(String[] args) {
Ticket ticket1 = new Ticket();
Ticket ticket2 = new Ticket();
Ticket ticket3 = new Ticket();
//开放三个柜台窗口,同时售卖票
new Thread(ticket1).start();
new Thread(ticket2).start();
new Thread(ticket3).start();
}
}
class Ticket extends Thread{
//总票数
private static int TicketNums = 100;
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"售票, 剩余票数"+(--TicketNums));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (TicketNums<=0){
break;
}
}
}
}
结果:
可以看出,虽然三个线程同时开启了卖票,但是当门票售罄的时候,线程仍然在卖票,存在着严重的问题。
二、通过实现Runnable接口
package com.wfy.ReviewThread;
public class Runnable02 {
public static void main(String[] args) {
Ticket1 ticket1 = new Ticket1();
Ticket1 ticket2 = new Ticket1();
Ticket1 ticket3 = new Ticket1();
//开启三个售票窗口
new Thread(ticket1).start();
new Thread(ticket2).start();
new Thread(ticket3).start();
}
}
class Ticket1 implements Runnable{
//设置共享总票数
private static int TicketCount=100;
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"窗口出售了一张门票 剩余门票数"+(--TicketCount));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (TicketCount<=0){
System.out.println("门票售罄,请下次再买");
break;
}
}
}
}
结果:
可以看出,使用Runnable接口同样也会出现相同的问题,不同的窗口抢占同一张门票
导致上述问题的原因
原因:同一时刻,三个线程同时检测出了这张门票,同时对这张门票进行抢占,所以造成了上述问题。
龟兔赛跑案例(多线程)
package com.wfy.ReviewThread;
public class RaceTest implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//判断比赛是否结束
boolean flag = gameOver(i);
if (flag){
System.out.println("比赛结束");
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
if (Thread.currentThread().getName().equals("兔子") && i%10 == 0 ){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
private boolean gameOver (int step){
if (winner !=null){
return true;
}
else {
if (step>=100){
winner=Thread.currentThread().getName();
System.out.println("获胜者是"+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
RaceTest raceTest = new RaceTest();
new Thread(raceTest,"乌龟").start();
new Thread(raceTest,"兔子").start();
}
}
三、线程常用的一些方法
3-1、线程退出
package com.wfy.ReviewThread;
public class Thread04 {
public static void main(String[] args) throws InterruptedException {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
Thread.sleep(10*1000);
threadTest.setClose(false);
}
}
class ThreadTest extends Thread{
private int count =0;
private boolean close=true;
@Override
public void run() {
while (close){
System.out.println(Thread.currentThread().getName()+"正在执行线程~"+(count++));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public void setClose(boolean close) {
this.close = close;
}
}
3-2、线程中断
interrput , 中断线程,但是并没有真正的结束线程。所以一般用于中断正在休眠线程
package com.wfy.ReviewThread;
public class Thread05 {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.setName("任凌飞");
t.setPriority(Thread.MIN_PRIORITY);//线程的优先级
t.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("线程处于休眠等待时间");
}
t.interrupt();//中断线程休眠
}
}
class T extends Thread{
@Override
public void run() {
while (true) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"努力的背英语单词~");
}
try {
System.out.println(Thread.currentThread().getName()+"很累,处于休息状态~");
Thread.sleep(5000);
} catch (InterruptedException e) {
//当该线程执行到一个interrupt方法时,就会catch一个异常
System.out.println(Thread.currentThread().getName()+"受到不明原因,中断了对英语的学习~");
}
}
}
}
3-3、线程插队
常用的两个方法:
- yield( ) : 线程的礼让,通过让出CPU,让其他线程执行,但是礼让的时间不确定,所以不一定能够礼让成功
- join( ) : 线程的插队。插队的线程一旦成功,则肯定先执行完插入的线程中的所有任务。
yield( ) 线程的礼让
代码展示:
package com.wfy.ReviewThread;
public class Thread06 {
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.start();
t2.setName("任凌飞");
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"学习java全栈知识的第"+i+"天");
if (i==5){
System.out.println("强制转换学习英语");
Thread.yield();//线程的礼让,不一定成功
}
}
}
}
class T2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"学习英语的第"+i+"天");
}
}
}
结果:
join( ) 线程插队
代码展示:
package com.wfy.ReviewThread;
public class Thread06 {
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.start();
t2.setName("任凌飞");
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"学习java全栈知识的第"+i+"天");
if (i==5){
System.out.println("强制转换学习英语");
t2.join();
}
}
}
}
class T2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"学习英语的第"+i+"天");
}
}
}
结果:
3-4、线程的优先级
Java提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
-
线程的优先级用数字表示,范围从1~10
* Thread.MIN_PRIORITY = 1 * Thread.MAX_PRIORITY = 10 * Thread.NORM_PRIORITY = 5
-
使用以下方式改变或获取优先级
getPriority().setPriority(int xxx) //优先级的设定建议在start()调度前
注意:优先级低只是意味着获取调度的概率低,并不是优先级低就不会被调用了。这都是需要看CPU的调度
3-5、用户线程
概念:用户线程(user-level threads)也叫工作线程,指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。
3-6、守护线程(Daemon)
概念:一般为工作线程服务的,当所有的用户线程结束,守护线程自动结束。
代码展示:
package com.wfy.ReviewThread;
public class Thread07 {
public static void main(String[] args) throws InterruptedException {
ThreadDaemon threadDaemon = new ThreadDaemon();
threadDaemon.setDaemon(true); //将threadDaemon设置为守护线程
threadDaemon.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("努力的学习Java");
}
}
}
class ThreadDaemon extends Thread{
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("努力的学习英语~");
}
}
}
结果:
四、线程的生命周期
线程可处于以下状态之一:
- NEW
尚未启动的线程处于此状态
- RUNNABLE
在Java虚拟机中执行的线程处于此状态
- BLOCKED
被阻塞等待监视器锁定的线程处于此状态
- WAITING
正在等待另一个线程执行特定动作的线程处于此状态
- TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- TERMINATED
已退出的线程处于此状态
五、线程的同步
- 在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
- 线程同步,即当一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
Synchronized(同步具体方法)
- 同步代码块
obj: 同步监视器
- obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中的代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
synchronized(obj){//得到对象的锁,才能操作同步代码
//需要被同步的代码
}
代码演示:
package com.wfy.ReviewThread;
//同步代码块
public class Runnable04 implements Runnable {
private int Tickets=100;
private boolean flag=true;
@Override
public void run() {
while (flag){
SellTickets();
}
}
public void SellTickets() {
synchronized (this){ //同步代码块
//判断
if (Tickets<=0){
System.out.println("门票售罄");
flag=false;
return;
}
System.out.println(Thread.currentThread().getName()+"进行门票销售 剩余门票"+(--Tickets));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
Runnable04 ticket = new Runnable04();
new Thread(ticket,"1号窗口").start();
new Thread(ticket,"2号窗口").start();
new Thread(ticket,"3号窗口").start();
}
}
- synchronized还可以放在方法声明中,表示整个方法-为同步方法
public synchronized void m(String name){
//需要被同步的代码
}
代码演示:
package com.wfy.ReviewThread;
public class Runnable03 implements Runnable {
//总票数
private int Ticket=100;
private boolean flag=true;
@Override
public void run() {
while (flag){
try {
SellTicket();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
//售票
public synchronized void SellTicket() throws InterruptedException {
//判断是否有剩余门票
if(Ticket==0){
System.out.println("门票售罄");
flag=false;
return;
}
System.out.println(Thread.currentThread().getName()+"售卖了 第"+(Ticket--)+"张门票");
Thread.sleep(50);
}
public static void main(String[] args) {
Runnable03 runnable = new Runnable03();
new Thread(runnable,"1号窗口").start();
new Thread(runnable,"2号窗口").start();
new Thread(runnable,"3号窗口").start();
}
}
六、Lock(锁)
死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步代码块同时拥有” 两个以上对象的锁“时,就可能产生死锁问题
代码演示死锁:
package com.wfy.ReviewThread;
public class Thread08 {
public static void main(String[] args) {
Person person1 = new Person(0, "小明");
Person person2 = new Person(1, "小红");
person1.start();
person2.start();
}
}
//学习
class Learn{
}
//打游戏
class Game{
}
class Person extends Thread{
//设置static,保证只获取一个对象
static Learn learn = new Learn();
static Game game = new Game();
int choice; //选择
String name;//姓名
Person(int choice , String name){
this.choice=choice;
this.name=name;
}
@Override
public void run() {
//学习或者游戏
try {
makeup();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//一个人只能在同一时间选择一中事情
public void makeup() throws InterruptedException {
if (choice==0){
synchronized (learn){ //获取学习的锁
System.out.println(this.name+"学习");
Thread.sleep(1000);
synchronized (game){ //获取游戏的锁
System.out.println(this.name+"打游戏");
}
}
}else {
synchronized (game){//获取打游戏的锁
System.out.println(this.name+"打游戏");
Thread.sleep(2000);
synchronized (learn){//获取学习的锁
System.out.println(this.name+"学习");
}
}
}
}
}
此时,由于小明和小红分别抢占了学习锁和游戏锁,相互之间都想获取另外一个锁,此时就陷入了死锁的状态
解决方法:
package com.wfy.ReviewThread;
public class Thread08 {
public static void main(String[] args) {
Person person1 = new Person(0, "小明");
Person person2 = new Person(1, "小红");
person1.start();
person2.start();
}
}
//学习
class Learn{
}
//打游戏
class Game{
}
class Person extends Thread{
//设置static,保证只获取一个对象
static Learn learn = new Learn();
static Game game = new Game();
int choice; //选择
String name;//姓名
Person(int choice , String name){
this.choice=choice;
this.name=name;
}
@Override
public void run() {
//学习或者游戏
try {
makeup();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//一个人只能在同一时间选择一中事情
public void makeup() throws InterruptedException {
if (choice==0){
synchronized (learn){ //获取学习的锁
System.out.println(this.name+"学习");
Thread.sleep(1000);
}
synchronized (game){ //获取游戏的锁
System.out.println(this.name+"打游戏");
}
}else {
synchronized (game){//获取打游戏的锁
System.out.println(this.name+"打游戏");
Thread.sleep(2000);
}
synchronized (learn){//获取学习的锁
System.out.println(this.name+"学习");
}
}
}
}
为线程设置休眠时间,让两个线程分开执行
产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
解决方法:打破其中的任意一个或多个条件就可以避免死锁的发生
Lock(锁)
- 从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
- java.util.concurrent.locks .Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享 资源之前应先获得Lock对象
- ReentrantLock类实现了Lock,它拥有synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式枷锁、释放锁
代码演示:
package com.wfy.ReviewThread;
import java.util.concurrent.locks.ReentrantLock;
public class Lock01 {
public static void main(String[] args) {
TicketCard ticketCard = new TicketCard();
new Thread(ticketCard).start();
new Thread(ticketCard).start();
new Thread(ticketCard).start();
}
}
class TicketCard implements Runnable{
private int Tickets=10;
private static ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
reentrantLock.lock(); //上锁
if (Tickets > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Tickets--);
}else{
break;
}
} finally {
reentrantLock.unlock(); //开锁
}
}
}
}
synchronized与Lock的对比
- Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多子类)
- 优先使用顺序:
- Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)
线程协作——生产者和消费者问题
线程通信
- Java提供了几个方法解决线程之间通讯的问题
方法名 | 作用 |
---|---|
wait( ) | 表示线程一直等待,直到其他线程通知,以sleep不同,会释放锁 |
wait( long timeout) | 指定等待的毫秒数 |
notify( ) | 唤醒一个处于等待状态的线程 |
notifyAll( ) | 唤醒同一个对象上所有调用wait( )方法的线程,优先级别高的线程优先调度 |
注意:均属于Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常llegalMonitorStateException
利用管程法解决生产者消费者问题
package com.wfy.ReviewThread;
//利用管程法解决生产者消费者问题
public class Lock02 {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Producer(synContainer).start();
new Customer(synContainer).start();
}
}
//生产者
class Producer extends Thread{
SynContainer container;
public Producer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 1; i <=100; i++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Customer extends Thread{
SynContainer container;
public Customer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
Chicken chicken= container.get();
System.out.println("消费了第"+i+"只鸡"+chicken.id);
}
}
}
//产品
class Chicken{
int id; //定义产品编号
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//需要一个容器
Chicken [] chickens = new Chicken[10];
//定义一个计数器
private int count=0;
//生产者生产产品
public synchronized void push( Chicken chicken) {
//判断容器是否已满
if (count==chickens.length){
System.out.println("容器已满,请先消费~");
try {
this.wait(); //生产者等待消费
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
chickens [count]=chicken;
count++;
//通知,生产者可以消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken get() {
//判断容器是否为空
if (count==0) {
System.out.println("容器为空,请等待生产者生产~");
try {
this.wait(); //消费者等待生产
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
count--;
Chicken chicken = chickens[count];
//消费完,通知生产者生产
this.notifyAll();
return chicken;
}
}
利用信号灯法解决生产者消费者问题
package com.wfy.ReviewThread;
//利用信号灯法解决生产者和消费者问题
public class Thread09 {
public static void main(String[] args) {
Games games = new Games();
new GameProcessor(games).start();
new Player(games).start();
}
}
//生产者 游戏商
class GameProcessor extends Thread{
Games games;
public GameProcessor(Games games){
this.games=games;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
games.publish("王者荣耀");
}
games.publish("原神");
}
}
}
//消费者 玩家
class Player extends Thread{
Games games;
public Player(Games games){
this.games=games;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
games.play();
}
}
}
//产品 游戏
class Games extends Thread{
//游戏制作时,玩家等待上架 T
//玩家游玩时,游戏商等待评价 F
String games; //游戏
boolean flag = true;
//发行
public synchronized void publish(String games){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("游戏商发行了游戏"+games);
//通知玩家游玩
this.notifyAll();
this.games=games;
this.flag = ! this.flag;
}
//游玩
public synchronized void play(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("玩家游玩游戏");
//通知游戏商看评论
this.notifyAll();
this.flag = ! this.flag;
}
}
八、线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动
-
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能的影响很大
-
组成部分:
1、线程池管理器(ThreadPoolManager):用于创建并管理线程池
2、工作线程(WorkThread): 线程池中线程
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
4、任务队列:用于存放没有处理的任务。提供一种缓冲机制。
- 便于管理线程:
- corePoolSize: 核心池大小
- maximumPoolSize: 最大线程数
- KeepAliveTime: 线程没有任务时最多保持多长时间后终止
- JDK5.0起提供了线程池相关的API:ExecutorService和Exectors
- ExecutorService:真正的线程池接口。
- Exectors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
代码演示线程池:
package com.wfy.ReviewThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为线程大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}