-
package com.hspedu.method; /** * @author 韩顺平 * @version 1.0 */ public class ThreadMethod01 { public static void main(String[] args) throws InterruptedException { //测试相关的方法 T t = new T(); t.setName("老韩"); t.setPriority(Thread.MIN_PRIORITY);//设置优先级1 t.start();//启动子线程 //主线程打印5 hi ,然后我就中断 子线程的休眠 for(int i = 0; i < 5; i++) { Thread.sleep(1000); System.out.println("hi " + i); } System.out.println(t.getName() + " 线程的优先级 =" + t.getPriority());//1 t.interrupt();//当执行到这里,就会中断 t线程的休眠. } } class T extends Thread { //自定义的线程类 @Override public void run() { while (true) { for (int i = 0; i < 100; i++) { //Thread.currentThread().getName() 获取当前线程的名称 System.out.println(Thread.currentThread().getName() + " 吃包子~~~~" + i); } try { System.out.println(Thread.currentThread().getName() + " 休眠中~~~"); Thread.sleep(20000);//20秒 } catch (InterruptedException e) { //当该线程执行到一个interrupt 方法时,就会catch 一个 异常, 可以加入自己的业务代码 //InterruptedException 是捕获到一个中断异常. System.out.println(Thread.currentThread().getName() + "被 interrupt了"); } } } }
中断只中断一次
-
package com.hspedu.method; /** * @author 韩顺平 * @version 1.0 */ public class ThreadMethod02 { public static void main(String[] args) throws InterruptedException { T2 t2 = new T2(); t2.start(); for(int i = 1; i <= 20; i++) { Thread.sleep(1000); System.out.println("主线程(小弟) 吃了 " + i + " 包子"); if(i == 5) { System.out.println("主线程(小弟) 让 子线程(老大) 先吃"); //join, 线程插队 //t2.join();// 这里相当于让t2 线程先执行完毕 Thread.yield();//礼让,不一定成功.. System.out.println("线程(老大) 吃完了 主线程(小弟) 接着吃.."); } } } } class T2 extends Thread { @Override public void run() { for (int i = 1; i <= 20; i++) { try { Thread.sleep(1000);//休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程(老大) 吃了 " + i + " 包子"); } } }
-
用户线程和守护线程
-
用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
-
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
-
常见的守护线程:垃圾回收机制
-
-
package com.hspedu.method; /** * @author 韩顺平 * @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++) {//main线程 System.out.println("宝强在辛苦的工作..."); Thread.sleep(1000); } } } class MyDaemonThread extends Thread { public void run() { for (; ; ) {//无限循环 try { Thread.sleep(1000);//休眠1000毫秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~"); } } }
- 线程生命周期
- 线程状态。 线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。RUNNABLE
内核
在Java虚拟机中执行的线程处于此状态。Ready【准备好了】和Running【真正的在执行】两个状态BLOCKED
被阻塞等待监视器锁定的线程处于此状态。WAITING
正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。TERMINATED
已退出的线程处于此状态。
- 线程同步机制
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
- 同步具体方法
- 同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码 //需要被同步代码; } //synchronized还可以放在方法声明中,表示整个方法-为同步方法 public synchronized void m1() { //需要被同步的代码 }
- 同步代码块
-
互斥锁
-
java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
-
每个对象都对应一个可称为"互斥锁"的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
-
关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
-
同步的局限性:导致程序的执行效率要降低
-
同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
-
同步方法(静态的)的锁为当前类本身
-
-
互斥锁注意事项
-
同步方法如果没有使用static修饰:默认锁对象为this
-
如果方法使用static修饰,默认锁对象:当前类.class
-
实现的落地步骤:
-
需要先分析上锁的代码
-
选择同步代码块或同步方法
-
要求多个线程的锁对象为同一个即可!
-
-
- 解决售票不同步问题Synchronized
-
this是什么,SellTicket03.class又是什么package com.hspedu.syn; /** * @author 韩顺平 * @version 1.0 * 使用多线程,模拟三个窗口同时售票100张 */ public class SellTicket { public static void main(String[] args) { //测试 // SellTicket01 sellTicket01 = new SellTicket01(); // SellTicket01 sellTicket02 = new SellTicket01(); // SellTicket01 sellTicket03 = new SellTicket01(); // // //这里我们会出现超卖.. // sellTicket01.start();//启动售票线程 // sellTicket02.start();//启动售票线程 // sellTicket03.start();//启动售票线程 // System.out.println("===使用实现接口方式来售票====="); // SellTicket02 sellTicket02 = new SellTicket02(); // // new Thread(sellTicket02).start();//第1个线程-窗口 // new Thread(sellTicket02).start();//第2个线程-窗口 // new Thread(sellTicket02).start();//第3个线程-窗口 //测试一把 SellTicket03 sellTicket03 = new SellTicket03(); new Thread(sellTicket03).start();//第1个线程-窗口 new Thread(sellTicket03).start();//第2个线程-窗口 new Thread(sellTicket03).start();//第3个线程-窗口 } } //实现接口方式, 使用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) {///*synchronized*/把这的去掉了,加到这了 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));//1 - 0 - -1 - -2 } } @Override public void run() { while (loop) { sell();//sell方法是一共同步方法 } } } //使用Thread方式 // new SellTicket01().start() // new SellTicket01().start(); class SellTicket01 extends Thread { private static int ticketNum = 100;//让多个线程共享 ticketNum // public void m1() { // synchronized (this) { // System.out.println("hello"); // } // } @Override public void run() { while (true) { if (ticketNum <= 0) { System.out.println("售票结束..."); break; } //休眠50毫秒, 模拟 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum)); } } } //实现接口方式 class SellTicket02 implements Runnable { private int ticketNum = 100;//让多个线程共享 ticketNum @Override public void run() { while (true) { if (ticketNum <= 0) { System.out.println("售票结束..."); break; } //休眠50毫秒, 模拟 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2 } } }
- this是针对当前引用的对象,class对象就是原始对象本身
- 非静态方法可以使用this或者class对象来同步,而静态方法必须使用class对象来同步并且它们互不影响
-
- 线程的死锁:多个线程都占用了对方的锁资源,但不肯让,导致死锁
- 释放锁
- 例题
-
package com.hspedu.homework; import java.util.Scanner; /** * @author 韩顺平 * @version 1.0 * 1.在main方法中启动两个线程 * 2.第一个线程循环随机打印100以内的整数 * 3.直到第2个线程从键盘读取了“Q”命令 */ public class Homework01 { public static void main(String[] args) { A a = new A(); B b = new B(a);//一定要注意. a.start(); b.start(); } } //创建A线程类 class A extends Thread { private boolean loop = true; @Override public void run() { //输出1-100数字 while (loop) { System.out.println((int)(Math.random() * 100 + 1)); //休眠 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("a线程退出..."); } public void setLoop(boolean loop) {//可以修改loop变量 this.loop = loop; } } //直到第2个线程从键盘读取了“Q”命令 class B extends Thread { private A a;//必须有这个属性 private Scanner scanner = new Scanner(System.in); public B(A 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线程 a.setLoop(false); System.out.println("b线程退出."); break; } } } }
创建线程,重写run方法,start(),获取字符,注意B类构造器的参数,属性
-
package com.hspedu.homework; /** * @author 韩顺平 * @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.因为这里涉及到多个线程共享资源,所以我们使用实现Runnable方式 //2. 每次取出 1000 class T implements Runnable { private int money = 10000; @Override public void run() { while (true) { //解读 //1. 这里使用 synchronized 实现了线程同步 //2. 当多个线程执行到这里时,就会去争夺 this对象锁 //3. 哪个线程争夺到(获取)this对象锁,就执行 synchronized 代码块, 执行完后,会释放this对象锁 //4. 争夺不到this对象锁,就blocked ,准备继续争夺 //5. this对象锁是非公平锁. synchronized (this) {//不是静态选this或者static修饰选类名.class。 //判断余额是否够 if (money < 1000) { System.out.println("余额不足"); break; } money -= 1000; System.out.println(Thread.currentThread().getName() + " 取出了1000 当前余额=" + money); } //休眠1s try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
-