用户线程:也称为工作线程
守护线程:一般是为工作线程服务的,当所有的用户线程结束,
守护线程也自动结束
常见的守护线程:垃圾回收机制
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 {@Overridepublic void run() { // 无限循环for (int i = 1; ; i++) {try {Thread.sleep(1000); // 休眠50毫秒} catch (InterruptedException e) {e.printStackTrace();}System.out.println("马蓉和宋哲快乐聊天,哈哈哈~~~~" + i);}}
}
new
runnable 可运行的由内核进行调度
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 {@Overridepublic 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;}}
}
在多线程中 保证数据在任何一个时刻
最多由一个线程访问
当有一个线程对内存进行操作时,其他线程都不能对这个内存地址进行操作
实现线程同步的具体方法: 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;@Overridepublic 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) + "张");}@Overridepublic void run() {while (loop) {sell(); // sell方法是一个同步方法}}
}
效果如下:
- 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"); } }
静态同步方法,锁在
当前类
多个线程都占用了对方的锁资源,但不肯相让,导致死锁
如:妈妈让小明完成作业后,再玩手机
小明要先玩手机,再写作业
/*** 模拟线程死锁*/
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;}@Overridepublic void run() {// 业务逻辑分析// 1. flag 为 true , 线程A先得到o1对象锁,然后尝试去获取o2对象锁// 2. 线程A 得不到o2 对象锁,就会blocked// 3. flag 为 false , 线程B先得到o2对象锁,然后尝试去获取o1对象锁// 2. 线程B 得不到o1 对象锁,就会blockedif (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");}}}}
}
释放锁的时机:
- 当前线程的同步方法,同步代码块
执行结束
- 当前线程在同步代码块,同步方法中遇到
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;}@Overridepublic 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;}@Overridepublic 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;@Overridepublic 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();}}}
}
下一篇:高阶数据结构之图论