文章目录
- 1. 线程的声明周期
- 1.1 JDK 中用 Thread.State 枚举表示了线程的几种状态
- 1.2 线程状态转换图
- 2. 线程的同步
- 2.1 Synchronized 线程同步机制
- 3. 互斥锁
- 3.1 注意事项和细节
- 3.2 守护线程 setDaemon()方法
- 4. 线程的死锁
- 5. 释放锁
- 6. 课后练习
1. 线程的声明周期
1.1 JDK 中用 Thread.State 枚举表示了线程的几种状态
1.2 线程状态转换图
package com.xjz.state_;
/**
* @author xjz_2002
* @version 1.0
*/
public class ThreadState_ {
public static void main(String[] args) throws InterruptedException {
T t = new T();
System.out.println(t.getName() + "状态" + t.getState());
t.start();
while (Thread.State.TERMINATED != t.getState()){
System.out.println(t.getName() + "状态" + t.getState());
Thread.sleep(500);
}
}
}
class T extends Thread {
@Override
public void run() {
while (true) {
for (int i = 0; i < 10; i++) {
System.out.println("hi" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}
2. 线程的同步
2.1 Synchronized 线程同步机制
使用 synchronized 来解决超卖问题
package com.xjz.syn;
/**
* @author xjz_2002
* @version 1.0
* 使用多线程,模拟三个窗口同时售票 100 张 ==》 同步锁 synchronized 防止超卖
*/
public class SellTicket {
public static void main(String[] args) {
SellTicket03 sellTicket03 = new SellTicket03();
new Thread(sellTicket03).start(); //第 1 个线程-窗口
new Thread(sellTicket03).start(); //第 2 个线程-窗口
new Thread(sellTicket03).start(); //第 3 个线程-窗口
}
}
// 实现 Runnable 接口,使用 synchronized 实现线程同步
class SellTicket03 implements Runnable {
private int ticketNum = 100;//让多个线程共享 ticketNum
private boolean loop = true;
public synchronized void sell() { //同步方法,在同一时刻,只能有一个线程来执行 sell方法
if (ticketNum <= 0) {
System.out.println("售票结束.");
loop = false;
return;
}
//休眠 50毫秒,模拟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票"
+ "剩余票数=" + (--ticketNum));
}
@Override
public void run() {
while (loop) {
sell();
}
}
}
3. 互斥锁
package com.xjz.syn;
/**
* @author xjz_2002
* @version 1.0
* 使用多线程,模拟三个窗口同时售票 100 张 ==》 同步锁 synchronized 防止超卖
*/
public class SellTicket {
public static void main(String[] args) {
SellTicket03 sellTicket03 = new SellTicket03();
new Thread(sellTicket03).start(); //第 1 个线程-窗口
new Thread(sellTicket03).start(); //第 2 个线程-窗口
new Thread(sellTicket03).start(); //第 3 个线程-窗口
}
}
// 实现 Runnable 接口,使用 synchronized 实现线程同步
class SellTicket03 implements Runnable {
private int ticketNum = 100;//让多个线程共享 ticketNum
private boolean loop = true;//控制 run 方法变量
Object object = new Object();
//同步方法(静态的)的锁为当前类本身
//代码解读
//1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
//2. 如果在静态方法中,实现一个同步代码块.
/*
synchronized (SellTicket03.class) {
System.out.println("m2");
}
*/
public synchronized static void m1(){
}
public static void m2() {
synchronized(SellTicket03.class){
System.out.println("m2");
}
}
//说明
//1. public synchronized void sell() {} 就是一个同步方法
//2. 这时锁在 this 对象
//3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在 this 对象
public /*synchronized*/ void sell() { //同步方法,在同一时刻,只能有一个线程来执行 sell方法
synchronized (/*this*/ object) {
if (ticketNum <= 0) {
System.out.println("售票结束.");
loop = false;
return;
}
//休眠 50毫秒,模拟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票"
+ "剩余票数=" + (--ticketNum));
}
}
@Override
public void run() {
while (loop) {
sell();
}
}
}
3.1 注意事项和细节
3.2 守护线程 setDaemon()方法
package com.xjz.method;
/**
* @author xjz_2002
* @version 1.0
* 守护线程
*/
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
MyDaemonThread myDaemonThread = new MyDaemonThread();
//如果我们希望当 main线程结束后,子线程自动结束
//,只需将子线程设为 守护线程
myDaemonThread.setDaemon(true);
myDaemonThread.start();
for (int i = 1; i <= 10; i++) {
System.out.println("宝强在辛苦的工作..");
Thread.sleep(1000);
}
}
}
class MyDaemonThread extends Thread {
@Override
public void run() {
for (; ; ) {//无限循环
try {
Thread.sleep(1000);//休眠 1000毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("马蓉和宋喆在快乐的聊天,哈哈哈");
}
}
}
4. 线程的死锁
多个线程都占用了对方的锁资源,但不肯想让,导致了死锁,在编程里是一定要避免死锁的发生。
- 应用案例
- 妈妈:你先完成作业,才让你玩手机
- 小明:你先让我玩手机,我才完成作业
package com.xjz.syn;
/**
* @author xjz_2002
* @version 1.0
* 模拟死锁线程
*/
public class DeadLock_ {
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A 线程");
DeadLockDemo B = new DeadLockDemo(false);
B.setName("B 线程");
A.start();
B.start();
}
}
//线程
class DeadLockDemo extends Thread {
static Object o1 = new Object();//保证多线程,共享一个对象,这里使用 static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) { //构造器
this.flag = flag;
}
@Override
public void run() {
//下面业务逻辑的分析
//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
if (flag) {
synchronized (o1) {//对象互斥锁,下面就是同步代码
System.out.println(Thread.currentThread().getName() + "进入1");
synchronized (o2) {//这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + "进入2");
}
}
} else {
synchronized (o2) {//对象互斥锁,下面就是同步代码
System.out.println(Thread.currentThread().getName() + "进入3");
synchronized (o1) {//这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + "进入4");
}
}
}
}
}
5. 释放锁
- 下面操作会释放锁
- 下面操作不会释放锁
6. 课后练习
package com.xjz.homework;
import java.util.Scanner;
/**
* @author xjz_2002
* @version 1.0
*/
public class Homework01 {
public static void main(String[] args) {
A a = new A();
B b = new B(a);
a.start();
b.start();
}
}
/*
(1) 在 main方法中启动两个线程
(2) 第 1 个线程循环随机打印 100以内的整数
(3) 直到第 2 个线程从键盘读取了 “Q" 命令。
*/
class A extends Thread {
private boolean loop = true;
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while (loop) {
int intNum = (int) (Math.random() * 100) + 1;
System.out.println("A 线程" + intNum);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("a线程 退出");
}
}
// 直到第 2 个线程从键盘读取了 “Q" 命令。
class B extends Thread {
private A a;
private Scanner scanner = new Scanner(System.in);
public B(A a) {
this.a = a;
}
@Override
public void run() {
while (true) {
//接收到用户的输入
System.out.println("请输入你的指令(Q)表示退出:");
char key = scanner.next().toUpperCase().charAt(0);
if (key == 'Q'){
a.setLoop(false);
break;
}
}
System.out.println("b线程 退出");
}
}
package com.xjz.homework;
/**
* @author xjz_2002
* @version 1.0
*/
public class Homework02 {
public static void main(String[] args) {
T t = new T(); //切记,同一个对象创建多个线程
Thread thread1 = new Thread(t);
thread1.setName("t1");
Thread thread2 = new Thread(t);
thread2.setName("t2");
thread1.start();
thread2.start();
}
}
//1. 有两个用户分别从同一个卡上取钱(总额:10000)
//2. 每次都取 1000,当余额不足时,就不能取款了
//3. 不能出现 超取 线程 =》 线程同步问题
class T implements Runnable {
private static int balance = 10000;
@Override
public void run() {
while (true) {
//代码解读
//1. 这里使用 synchronized 实现了线程同步
//2. 当多个线程执行到这里时,就会去争夺 this 对象锁
//3. 哪个线程争夺到(获取) this 对象锁,就执行 synchronized 代码块,执行完后,会释放 this对象锁
//4. 争夺不到 this对象锁,就 blocked,准备继续争夺
//5. this 对象锁是非公平锁
synchronized (this) {
//判断余额是否够
if (balance < 1000) {
System.out.println("余额不足,无法取款");
break;
}
//取钱
balance -= 1000;
System.out.println(Thread.currentThread().getName() + "取出 1000,剩余" + balance);
}
//休眠 500ms
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}