韩顺平_循序渐进学Java零基础_第17章 多线程编程(P580 - P599)
第17章 多线程编程
580. 程序进程线程
进程是程序的一次执行过程,或是正在进行的一个程序。是动态过程,有其自身的产生、存在和消亡过程
线程是由进程创建的,是进程的一个实体;一个进程可以拥有多个线程
581. 并发并行
并发:同一个时刻,多个任务交替执行,造成一种 “貌似同时” 的错觉,简单地说,单核 CPU 实现的多任务就是并发
并行:同一个时刻,多个任务同时执行,多核 CPU 实现的多任务就是并行
Runtime runTime = Runtime . getRuntime ( ) ;
System . out. println ( runTime. availableProcessors ( ) ) ;
582. 继承Thread类创建线程
创建线程的两种方式:继承 Thread 类并重写 run() 方法;实现 Runnable 接口,重写 run() 方法
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() 方法,也即使用了静态代理设计模式
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. 多线程售票问题
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()
中断线程,线程并未消亡
public final static int MIN_PRIORITY = 1 ;
public final static int NORM_PRIORITY = 5 ;
public final static int MAX_PRIORITY = 10 ;
590. 线程插队
方法名
功能
yield()
线程礼让,让出 CPU 资源,当礼让时间不缺点,是否礼让成功不确定
join()
线程插队,一旦插队成功,则必须执行完插入线程的所有任务
591. 线程插队练习
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 ( ) ;
}
}
}
}
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. 守护线程
用户线程:也叫工作线程,当线程的任务执行完成或以通知方式结束
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。经典守护线程:垃圾回收机制
public class MyDaemon {
public static void main ( String [ ] args) throws InterruptedException {
Thread thread = new Thread ( new Daemon ( ) ) ;
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. 线程同步机制
595. 互斥锁
Java 语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应一个可称为 “互斥锁” 的标记,这个标记用来保证在任意时刻,有且仅有一个线程可以访问该对象。用关键字 synchronized 来与对象的互斥锁联系
同步的局限性:程序的执行效率降低
非静态同步方法的锁可以是它本身 this,也可以是其它对象(要求是同一个对象)
静态同步方法的锁是当前类本身(ClassName.class)
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 ( ) ;
}
}
}
}
}
class SellTicket implements Runnable {
public static void getTicket ( ) {
synchronized ( SellTicket . class ) {
System . out. println ( "售出一张票" ) ;
}
}
}
596. 线程死锁
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