多线程
1.守护线程
用户线程:也称为工作线程
守护线程:一般是为工作线程服务的,当所有的用户线程结束,
守护线程也自动结束
常见的守护线程:垃圾回收机制
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
MyDaemonThread myDaemonThread = new MyDaemonThread();
// 如果我们希望main线程结束后,子线程自动结束
// TODO:需要将子线程设为守护线程即可
myDaemonThread.setDaemon(true);
myDaemonThread.start();
for (int i = 1; i <= 10; i++) {
System.out.println("宝强在辛苦的工作~~~" + i);
Thread.sleep(1000);
}
System.out.println("主线程结束!!!");
}
}
class MyDaemonThread extends Thread {
@Override
public void run() {
// 无限循环
for (int i = 1; ; i++) {
try {
Thread.sleep(1000); // 休眠50毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("马蓉和宋哲快乐聊天,哈哈哈~~~~" + i);
}
}
}
2.线程的生命周期
-
new
-
runnable 可运行的由内核进行调度
- ready 就绪状态
- running 运行状态
-
blocked
-
terminated 终止状态
-
waiting
-
timed_waiting 超时等待
1.先进入new状态 2.再进入runnable状态 3.Thread.sleep()可能导致进入time_waiting状态 4.结束进程则进入terminated状态
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(1000); } System.out.println(t.getName() + " 的状态:" + t.getState()); } } 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; } } }
3.线程同步机制
-
在多线程中 保证数据在任何一个时刻
最多由一个线程访问
-
当有一个线程对内存进行操作时,其他线程都不能对这个内存地址进行操作
实现线程同步的具体方法:
Synchronized
- 同步代码块
synchronized (对象){ // 对象锁 // 需要被同步的代码 }
- 放在方法声明中 方法成为同步方法
pulbic synchronized void m(){ // 需要被同步的代码 }
未使用同步机制售票导致
超卖
public class SellTicket { public static void main(String[] args) { SellTicketThread sellTicketThread01 = new SellTicketThread(); SellTicketThread sellTicketThread02 = new SellTicketThread(); SellTicketThread sellTicketThread03 = new SellTicketThread(); sellTicketThread01.start(); sellTicketThread02.start(); sellTicketThread03.start(); } } class SellTicketThread extends Thread { private static int ticketNum = 100; @Override public void run() { while (true) { if (ticketNum <= 0) { System.out.println("窗口: " + Thread.currentThread().getName() + "售票结束!!!"); break; } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("窗口" + Thread.currentThread().getName() + ":售出一张票" + ",剩余" + (--ticketNum) + "张"); } } }
上述方法存在超卖问题:
使用
synchronzied
解决超卖问题public class SellTicket { public static void main(String[] args) { SellTicketThread02 sellTicketThread02 = new SellTicketThread02(); new Thread(sellTicketThread02).start(); new Thread(sellTicketThread02).start(); new Thread(sellTicketThread02).start(); } } class SellTicketThread02 implements Runnable { private int ticketNum = 100; private boolean loop = true; // 控制run() 的执行 public synchronized void sell() { // 同步方法,在同一时刻,只能有一个线程来执行sell方法 if (ticketNum <= 0) { System.out.println("窗口: " + Thread.currentThread().getName() + "售票结束!!!"); loop = false; return; } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("窗口" + Thread.currentThread().getName() + ":售出一张票" + ",剩余票数=" + (--ticketNum) + "张"); } @Override public void run() { while (loop) { sell(); // sell方法是一个同步方法 } } }
效果如下:
-
4.互斥锁
- java中引进了对象互斥锁来共享数据操作的完整性
- 对象使用互斥锁标记 保证任意时刻只能有一个线程访问该对象
- synchronized关键字与互斥锁联系
同步的局限性:导致程序执行效率降低
- 同步方法(静态):当前类.class
- 同步方法(非静态):可以是this,也可以是其他对象(要求是同一个对象)
synchronized
同步代码块
public void sell() {
synchronized (this) {
if (ticketNum <= 0) {
System.out.println("窗口: " + Thread.currentThread().getName() + "售票结束!!!");
loop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + ":售出一张票" + ",剩余票数=" + (--ticketNum) + "张");
}
}
public synchronized static void m1() { } //锁加在当前类 public static void m2() { synchronized (SellTicketThread02.class) { System.out.println("m2"); } }
静态同步方法,锁在
当前类
5.线程死锁
多个线程都占用了对方的锁资源,但不肯相让,导致死锁
如:妈妈让小明完成作业后,再玩手机
小明要先玩手机,再写作业
/** * 模拟线程死锁 */
public class DeadLock {
public static void main(String[] args) {
// 模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
DeadLockDemo B = new DeadLockDemo(false);
A.setName("线程A");
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 为 true , 线程A先得到o1对象锁,然后尝试去获取o2对象锁
// 2. 线程A 得不到o2 对象锁,就会blocked
// 3. flag 为 false , 线程B先得到o2对象锁,然后尝试去获取o1对象锁
// 2. 线程B 得不到o1 对象锁,就会blocked
if (flag) {
synchronized (o1) {
// 对象互斥锁 下面是同步代码
System.out.println(Thread.currentThread().getName() + " 进入1");
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入3");
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + " 进入4");
}
}
}
}
}
6.释放锁
释放锁的时机:
- 当前线程的同步方法,同步代码块
执行结束
- 当前线程在同步代码块,同步方法中遇到
break,return
- 当前线程在同步代码块,同步方法中出现了
未处理的Error或Exception
,导致异常结束- 当前线程在同步代码块,同步方法中执行了线程对象的
wait()方法
,当前线程暂停并释放锁不会释放锁的情况:
- 程序调用
Thread.sleep(),Thread.yield()
,暂停当前线程的执行,不会释放锁- 其他线程调用该线程的suspend()将线程挂起,并不释放锁
suspend()和resume()
过时了
思考题
import java.util.Scanner;
/** * 作业01 */
public class HomeWork01 {
public static void main(String[] args) {
PrintThread printThread = new PrintThread();
ReadThread readThread = new ReadThread(printThread);
printThread.start();
readThread.start();
}
}
class PrintThread extends Thread {
private boolean loop = true;
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while (loop) {
System.out.println((int) (Math.random() * 100 + 1));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Print线程退出!!!");
}
}
// 需要获取PrintThread
class ReadThread extends Thread {
private PrintThread printThread;
private Scanner sc = new Scanner(System.in);
public ReadThread(PrintThread printThread) {
this.printThread = printThread;
}
@Override
public void run() {
while (true) {
// 接收用户输入
System.out.println("请输入你的指令(Q 表示退出):");
char key = sc.next().toUpperCase().charAt(0);// 获取输入的第一个字符并转成大写
if ('Q' == key) {
// 以通知的方式结束PrintThread线程
printThread.setLoop(false);
System.out.println("Read线程结束!!!");
break;
}
}
}
}
如果将 syschronzied 同步代码块时有
Thread.sleep()
导致互斥锁始终被第一个线程Thread-0占有,导致Thread-1超取
@Override
public void run() {
while (true) {
synchronized (this) {
money -= 1000;
System.out.println("用户:" + Thread.currentThread().getName() + " 取走1000" + ",余额=" + money);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (money <= 0) {
System.out.println("余额不足,结束取款!!!");
break;
}
}
}
正确做法如下 synchronized 只修饰需要互斥的动作,不修饰进程的sleep 注:
synchronized是非公平锁
/** * 作业02 */
public class HomeWork02 {
public static void main(String[] args) {
GetMoney getMoney = new GetMoney();
new Thread(getMoney).start();
new Thread(getMoney).start();
}
}
// 多线程共享资源 一般实现 Runnable接口
class GetMoney implements Runnable {
private int money = 10000;
@Override
public void run() {
while (true) {
if (money <= 0) {
System.out.println("用户:" + Thread.currentThread().getName() + " 余额不足,结束取款!!!");
break;
}
synchronized (this) {
money -= 1000;
System.out.println("用户:" + Thread.currentThread().getName() + " 取走1000" + ",余额=" + money);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
文章评论