韩顺平循序渐进学Java零基础 第17章 多线程编程
Posted Spring-_-Bear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了韩顺平循序渐进学Java零基础 第17章 多线程编程相关的知识,希望对你有一定的参考价值。
第17章 多线程编程
580. 程序进程线程
- 进程是程序的一次执行过程,或是正在进行的一个程序。是动态过程,有其自身的产生、存在和消亡过程
- 线程是由进程创建的,是进程的一个实体;一个进程可以拥有多个线程
581. 并发并行
- 并发:同一个时刻,多个任务交替执行,造成一种 “貌似同时” 的错觉,简单地说,单核 CPU 实现的多任务就是并发
- 并行:同一个时刻,多个任务同时执行,多核 CPU 实现的多任务就是并行
// 获取系统可用处理器个数
Runtime runTime = Runtime.getRuntime();
System.out.println(runTime.availableProcessors());
582. 继承Thread类创建线程
- 创建线程的两种方式:继承 Thread 类并重写 run() 方法;实现 Runnable 接口,重写 run() 方法
/**
* @author Spring-_-Bear
* @version 2021-11-21 19:53
*/
public class ThreadEx extends Thread
public static void main(String[] args)
new Dog().start();
for (int i = 1; i <= 10; i++)
try
System.out.println(Thread.currentThread().getName() + i);
sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
class Dog extends Thread
@Override
public void run()
while (true)
try
System.out.println(Thread.currentThread().getName() + ":汪汪汪~~~");
sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
- 当启动程序时理解为开启了一个进程,进程开启了 main 线程,在 main 线程中可以开启其它线程,只有当所有线程都消亡时,进程才结束
583. 多线程机制
584. 为什么是start
- new Dog().run() 与 new Dog().start() 的区别:前者只是单纯地调用 run() 方法,而后者启用了线程,在 start() 方法中实际调用了 start0() 方法,这是个 native 修饰的方法,真正实现了启动线程
- start() 方法调用了 start0() 方法后,该线程并不一定会马上执行,只是将线程变成了可运行状态,具体什么时候执行,取决于 CPU
585. Runnable创建线程
- Java 是单继承机制,若某个类已经继承了某个父类,这时只能通过实现 Runnable 接口实现线程
- 实现 Runnable 接口的类不能直接调用 start() 方法,可以将对象作为 Thread(Runnable) 的参数,从而调用 start() 方法,也即使用了静态代理设计模式
/**
* @author Spring-_-Bear
* @version 2021-11-21 20:50
*/
public class Thread02
public static void main(String[] args)
// 静态代理设计模式
new Thread(new Cat()).start();
class Cat implements Runnable
@Override
public void run()
while (true)
System.out.println("喵喵喵~~~");
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
586. 多个子线程案例
- 实现 Runnable 接口方式更加适合多个线程共享某个资源的情况,并且避免了单继承的显示
Cat cat = new Cat();
Thread thread1 = new Thread(cat);
Thread thread2 = new Thread(cat);
587. 多线程售票问题
/**
* @author Spring-_-Bear
* @version 2021-11-21 21:48
*/
public class Ticket
public static void main(String[] args)
SellTicket sellTicket = new SellTicket();
Thread thread = new Thread(sellTicket);
Thread thread1 = new Thread(sellTicket);
Thread thread2 = new Thread(sellTicket);
thread.start();
thread1.start();
thread2.start();
class SellTicket implements Runnable
private int ticketNum = 100;
@Override
public void run()
while (true)
if (ticketNum <= 0)
System.out.println("售票结束···");
break;
try
Thread.sleep(100);
catch (InterruptedException e)
e.printStackTrace();
System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票,余票:" + (--ticketNum));
588. 通知线程退出
- 通过使用变量来控制 run 方法退出的方式停止线程,即通知方式
589. 线程中断
方法名 | 功能 |
---|---|
setName(String) | 设置线程名 |
getName() | 获取线程名 |
start() | 启动线程 |
run() | 调用线程对象的 run() 方法 |
setPriority(int) | 设置线程优先级 |
getPriority() | 获得线程优先级 |
sleep(long) | 休眠线程 |
interrupt() | 中断线程,线程并未消亡 |
- 线程优先级
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
590. 线程插队
方法名 | 功能 |
---|---|
yield() | 线程礼让,让出 CPU 资源,但礼让时间不确定,是否礼让成功不确定,取决于 os |
join() | 线程插队,一旦插队成功,则必须执行完插入线程的所有任务 |
591. 线程插队练习
/**
* @author Spring-_-Bear
* @version 2021-11-21 22:49
*/
public class JoinEx
public static void main(String[] args) throws InterruptedException
Thread temp = new Thread(new Temp());
temp.start();
for (int i = 1; i <= 20; i++)
System.out.println(Thread.currentThread().getName() + ":" + i);
Thread.sleep(1000);
if (i == 5)
temp.join();
// main 线程礼让,不一定礼让成功
// Thread.yield();
class Temp implements Runnable
@Override
public void run()
for (int i = 1; i <= 20; i++)
System.out.println(Thread.currentThread().getName() + ":" + i);
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
592. 守护线程
- 用户线程:也叫工作线程,当线程的任务执行完成或以通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。经典守护线程:垃圾回收机制
/**
* @author Spring-_-Bear
* @version 2021-11-22 19:10
*/
public class MyDaemon
public static void main(String[] args) throws InterruptedException
Thread thread = new Thread(new Daemon());
// 设置为 main 线程的守护线程
thread.setDaemon(true);
thread.start();
Thread.sleep(5000);
System.out.println("妈妈回家了,小明结束写作业!");
class Daemon implements Runnable
@Override
public void run()
while (true)
System.out.println("小明写作业中···");
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
593. 线程7大状态
状态 | 说明 |
---|---|
NEW | 尚未启动的线程处于此状态。 该状态线程对象被创建,但还未调用start方法 |
RUNNABLE | 可运行线程的线程状态,细分为 Running 和 Ready 两个状态。处于可运行状态的线程可能正在 Java 虚拟机中执行,也可能正在等待来自操作系统的其他资源 |
BLOCKED | 被阻塞等待监视器锁的线程处于此状态。处于该状态的线程正在等待获取一个监视器锁进入同步代码或方法;也可能是在调用了Object.wait后等待一个监视器锁重新进入同步代码或方法 |
WAITING | 线程处于等待状态;处于该状态的线程可能是因为调用了Object.wait()、Thread.join()、LockSupport.park() 中的某一个方法;处于该状态的线程正在等待其他线程完成一些特定的操作 |
TIMED_WAITING | 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。线程处于定时等待状态,这个等待是有具有指定时间的;处于这个状态的线程可能是调用了具有指定时间的 Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()、LockSupport.parkUntil() |
TERMINATED | 已退出的线程处于此状态 |
594. 线程同步机制
-
线程同步:当有一个线程在对内存进行操作时,其它线程都不可以对这个内存进行操作,直到该线程完成操作,其它线程才能对该内存进行操作,也即同一时刻只允许一个线程操作
-
得到对象的锁,才可以操作对象的代码;也可以将 synchronized 加在方法声明中,表示整个方法为同步方法
595. 互斥锁
- Java 语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应一个可称为 “互斥锁” 的标记,这个标记用来保证在任意时刻,有且仅有一个线程可以访问该对象。用关键字 synchronized 来与对象的互斥锁联系
- 同步的局限性:程序的执行效率降低
- 非静态同步方法的锁可以是它本身 this,也可以是其它对象(要求是同一个对象)
- 静态同步方法的锁是当前类本身(ClassName.class)
/**
* @author Spring-_-Bear
* @version 2021-11-21 21:48
*/
public class Ticket
public static void main(String[] args)
SellTicket sellTicket = new SellTicket();
Thread thread1 = new Thread(sellTicket);
Thread thread2 = new Thread(sellTicket);
Thread thread3 = new Thread(sellTicket);
thread1.start();
thread2.start();
thread3.start();
class SellTicket implements Runnable
private int ticketNum = 100;
Object object = new Object();
@Override
public void run()
sell();
public void sell()
while (true)
synchronized (this)
if (ticketNum <= 0)
System.out.println("售票结束···");
break;
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票,余票:" + (--ticketNum));
try
Thread.sleep(100);
catch (InterruptedException e)
e.printStackTrace();
/*
* 由于主方法中只创建了一个 SellTicket 对象,所以线程 0、1、2 操作的都是同一个对象
* 因而将锁加在对象本身等价于将锁将在此对象的其它对象(字段),也即下面的两段代码等价
* synchronized (this) <=> synchronized (object)
*/
- 静态方法中的互斥锁加在类上
class SellTicket implements Runnable
public static void getTicket()
synchronized (SellTicket.class)
System.out.println("售出一张票");
596. 线程死锁
/**
* @author Spring-_-Bear
* @version 2021-11-22 21:02
*/
public class DeadLockDemo
public static void main(String[] args)
new DeadLock(true).start();
new DeadLock(false).start();
class DeadLock extends Thread
static Object object1 = new Object();
static Object object2 = new Object();
boolean flag;
public DeadLock(boolean flag)
this.flag = flag;
@Override
public void run()
if (flag)
synchronized (object1)
System.out.println(Thread.currentThread().getName() + " 获得对象 1 的锁,尝试获取对象 2 的锁···");
synchronized (object2)
System.out.println(Thread.currentThread().getName() + " 成功获得对象 2 的锁!");
else
synchronized (object2)
System.out.println(Thread.currentThread().getName() + " 成功获得对象 2 的锁,尝试获取对象 1 的锁···");
synchronized (object1)
System.out.println(Thread.currentThread().getName() + " 成功获得对象 1 的锁!");
597. 释放锁
- 当前线程的同步方法、同步代码块执行结束会释放锁
- 当前线程在同步方法、同步代码块中遇到 break、return 会释放锁
- 当前线程在同步方法、同步代码块中出现了未处理的 Error 或 Exception 导致结束会释放锁
- 当前线程在同步方法、同步代码块中执行了线程对象的 wait() 方法,当前线程暂停,并释放锁
- 当前线程在执行同步代码块、同步方法的过程中调用了 Thread.sleep()、Thread.yield() 方法暂停当前线程的执行,不会释放锁
- 线程执行同步代码块时,其它线程调用了该线程的 suspend() 方法将该方法挂起,该线程不会释放锁。suspend()、resume() 方法均已过时,不再推荐使用
598. 线程家庭作业1
599. 线程家庭作业2
以上是关于韩顺平循序渐进学Java零基础 第17章 多线程编程的主要内容,如果未能解决你的问题,请参考以下文章