目录
二、继承 Thread vs 实现 Runnable 的区别
1、JDK 中用 Thread.State 枚举表示了线程的几种状态
一、线程基本使用
1、创建线程的两种方式
2、线程应用案例1-继承 Thread 类
public class Thread01 { public static void main(String[] args) throws InterruptedException { //创建 Cat 对象,可以当做线程使用 Cat cat = new Cat(); // 读源码 /*(1) public synchronized void start () { start0(); } (2) // start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现 // 真正实现多线程的效果, 是 start0(), 而不是 run private native void start0();*/ cat.start();//启动线程-> 最终会执行 cat 的 run 方法 //cat.run();//run 方法就是一个普通的方法, 没有真正的启动一个线程,就会把 run 方法执行完毕,才向下执行 //说明: 当 main 线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行 //这时 主线程和子线程是交替执行.. System.out.println("主线程继续执行" + Thread.currentThread().getName());//名字 main for (int i = 0; i < 60; i++) { System.out.println("主线程 i=" + i); //让主线程休眠 Thread.sleep(1000); } } } // 说明 // 1. 当一个类继承了 Thread 类, 该类就可以当做线程使用 // 2. 我们会重写 run 方法,写上自己的业务代码 // 3. run Thread 类 实现了 Runnable 接口的 run 方法 /*@Override public void run() { if (target != null) { target.run(); } }*/ class Cat extends Thread { int times = 0; @Override public void run() {//重写 run 方法,写上自己的业务逻辑 while (true) { //该线程每隔 1 秒。在控制台输出 “喵喵, 我是小猫咪” System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName()); //让该线程休眠 1 秒 ctrl+alt+t try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (times == 80) { break;//当 times 到 80, 退出 while, 这时线程也就退出.. } } } } }
3、线程应用案例-实现 Runnable 接口
// package com.hspedu.threaduse; 这个导包在运行时是必须的 public class Thread02 { public static void main(String[] args) { Dog dog = new Dog(); //dog.start(); 这里不能调用start //创建了Thread对象,把 dog对象(实现Runnable),放入Thread Thread thread = new Thread(dog); thread.start(); // Tiger tiger = new Tiger();//实现了 Runnable // ThreadProxy threadProxy = new ThreadProxy(tiger); // threadProxy.start(); } } class Animal { } class Tiger extends Animal implements Runnable { @Override public void run() { System.out.println("老虎嗷嗷叫...."); } } //线程代理类 , 模拟了一个极简的Thread类 class ThreadProxy implements Runnable {//你可以把Proxy类当做 ThreadProxy private Runnable target = null;//属性,类型是 Runnable @Override public void run() { if (target != null) { target.run();//动态绑定(运行类型Tiger) } } public ThreadProxy(Runnable target) { this.target = target; } public void start() { start0();//这个方法时真正实现多线程方法 } public void start0() { run(); } } class Dog implements Runnable { //通过实现Runnable接口,开发线程 int count = 0; @Override public void run() { //普通方法 while (true) { System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName()); //休眠1秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (count == 10) { break; } } } }
4、线程使用应用案例-多线程执行
// package com.hspedu.threaduse; 这个导包在运行的时候是必须的 /** * main线程启动两个子线程 */ public class Thread03 { public static void main(String[] args) { T1 t1 = new T1(); T2 t2 = new T2(); Thread thread1 = new Thread(t1); Thread thread2 = new Thread(t2); thread1.start();//启动第1个线程 thread2.start();//启动第2个线程 //... } } class T1 implements Runnable { int count = 0; @Override public void run() { while (true) { //每隔1秒输出 “hello,world”,输出10次 System.out.println("hello,world " + (++count)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(count == 60) { break; } } } } class T2 implements Runnable { int count = 0; @Override public void run() { //每隔1秒输出 “hi”,输出5次 while (true) { System.out.println("hi " + (++count)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(count == 50) { break; } } } }
5、线程如何理解
二、继承 Thread vs 实现 Runnable 的区别
1、代码案例
//package com.hspedu.ticket; 这个导包在运行时是必须导入的 /** * 使用多线程,模拟三个窗口同时售票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个线程-窗口 } } //使用Thread方式 class SellTicket01 extends Thread { private static 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)); } } } //实现接口方式 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 } } }
三、线程终止
1、基本说明
2、应用案例
public class ThreadExit_ { public static void main(String[] args) throws InterruptedException { T t1 = new T(); t1.start(); //如果希望main线程去控制t1 线程的终止, 必须可以修改 loop //让t1 退出run方法,从而终止 t1线程 -> 通知方式 //让主线程休眠 10 秒,再通知 t1线程退出 System.out.println("main线程休眠10s..."); Thread.sleep(10 * 1000); t1.setLoop(false); } } class T extends Thread { private int count = 0; //设置一个控制变量 private boolean loop = true; @Override public void run() { while (loop) { try { Thread.sleep(50);// 让当前线程休眠50ms } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T 运行中...." + (++count)); } } public void setLoop(boolean loop) { this.loop = loop; } }
四、线程常用方法
1、常用方法第一组
2、注意事项和细节
3、常用方法第二组
package com.hspedu.method; 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 + " 包子"); } } }
4、用户线程和守护线程
package com.hspedu.method; 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("马蓉和宋喆快乐聊天,哈哈哈~~~"); } } }
五、线程的生命周期
1、JDK 中用 Thread.State 枚举表示了线程的几种状态
2、线程状态转换图
3、写程序查看线程状态
package com.hspedu.state_; 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); } 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; } } }
六、线程的同步(Synchronized)
1、线程同步机制
2、同步具体方法-Synchronized
3、分析同步原理
七、互斥锁
1、基本介绍
2、使用互斥锁来解决售票问题
代码:两种方法,代码块加锁,和方法上加锁
package com.hspedu.syn; /** * 使用多线程,模拟三个窗口同时售票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) { 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 } } }
3、注意事项和细节
八、线程的死锁
1、基本介绍
如:
package com.hspedu.syn; /** * 模拟线程死锁 */ 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"); } } } } }
九、释放锁
1、下面操作会释放锁
2、下面操作不会释放锁
我是吕同学,祝自己也祝您变强了~