581,多线程机制
因为需要敌人的坦克可以自由移动并发射子弹,我们的坦克可以移动并发射子弹,这些要用到线程的知识。
根据JConsole监控线程执行情况,发现,主线程执行完了,子线程还没有执行完,并不能表示当前进程死亡了,只有当所有的子线程执行完了,主进程才会结束。
真正实现多线程的效果, 是 start0(), 而不是 run。
package com.hspedu.threaduse; //演示通过继承 Thread 类创建线程 public class Thread01 { public static void main(String[] args) throws InterruptedException { Runtime runtime = Runtime.getRuntime(); //获取当前电脑的cpu数量/核心数 int cpuNums = runtime.availableProcessors(); System.out.println("当前有cpu 个数=" + cpuNums); //创建 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 方法 class Cat extends Thread { int times = 0; @Override public void run() {//重写 run 方法, 写上自己的业务逻辑 //该线程每隔 1 秒。 在控制台输出 “喵喵, 我是小猫咪 while (true) { System.out.println("喵喵,我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName()); try { //让该线程休眠 1 秒 ctrl+alt+t Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(times == 80) { break;//当 times 到 80, 退出 while, 这时线程也就退出.. } } } }
584,Runnable创建线程和静态代理模式
代码还模拟了静态代理模式
package com.hspedu.threaduse; //通过实现接口 Runnable 来开发线程 public class Thread02 { public static void main(String[] args) { // Dog dog = new Dog(); // //dog.start(); 这里不能调用 start,Runnable没有start方法 // // //创建了 Thread 对象, 把 dog 对象(实现 Runnable),放入 Thread // Thread thread = new Thread(dog); // thread.start(); Tiger tiger = new Tiger(); ThreadProxy threadProxy = new ThreadProxy(tiger);//接口的多态:接口的引用可以指向实现该接口的类 threadProxy.start(); } } class Animal {} class Tiger extends Animal implements Runnable { @Override public void run() { System.out.println("老虎嗷嗷叫..."); } } //线程代理类 , 模拟了一个极简的 Thread 类,就是你可以把 ThreadProxy类当作 Thread类 class ThreadProxy implements Runnable { private Runnable target = null;//属性, 类型是 Runnable @Override public void run() { if(target != null) { target.run();//动态绑定(运行类型Tiger) } } public ThreadProxy(Runnable target) {//形参是Tiger类对象 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()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(count == 10) { break; } } } }
585,多个子线程案例
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 == 10) { break; } } } } class T2 implements Runnable { int count = 0; @Override public void run() { while (true) { //每隔 1 秒输出 “hi” ,输出 5 次 System.out.println("hi " + (++count)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(count == 5) { break; } } } }
586,多线程售票问题
会有负票出现,原因是3个进程同时抢一个票,票已经卖为0了,但是不能阻止售卖。
package com.hspedu.threaduse; //使用多线程, 模拟三个窗口同时售票 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();//第 1 个线程-窗口 new Thread(sellTicket02).start();//第 1 个线程-窗口 } } //使用 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)); } } }
587,通知线程退出
package com.hspedu.threaduse; public class ThreadExit { public static void main(String[] args) throws InterruptedException { T t = new T(); t.start(); //如果希望main线程去控制 t1线程的终止,必须可以修改 loop //让 t1 退出run方法,从而终止 t1线程 -> 这叫 通知方式 //让主线程休眠 2 秒,再通知 t1 线程退出 System.out.println("main线程休眠2s..."); Thread.sleep(2 * 1000); t.setLoop(false); } } //每隔50毫秒输出一句话 class T extends Thread { private int count = 0; //设置一个控制变量,如果loop为false,就退出循环 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; } }
588,线程中断
package com.hspedu.threaduse; public class ThreadMethod01 { public static void main(String[] args) throws InterruptedException { A a = new A(); a.setName("老韩"); a.setPriority(Thread.MIN_PRIORITY);//1 a.start();//启动子线程 //主线程打印 5 次 hi,然后我就中断 子线程的休眠 for(int i = 0; i < 5; i++) { Thread.sleep(1000); System.out.println("hi " + i); } System.out.println(a.getName() + " 线程的优先级 = " + a.getPriority()); a.interrupt();//当执行到这里,就会中断 t 线程的休眠 } } class A extends Thread {//自定义的线程类 @Override public void run() { while (true) { for (int i = 0; i < 10; 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了"); } } } }
589,线程插队
package com.hspedu.threaduse; public class ThreadMethod02 { public static void main(String[] args) throws InterruptedException { B b = new B(); b.start(); for (int i = 1; i <= 20; i++) { Thread.sleep(1000); System.out.println("主线程(小弟)吃了 " + i +" 包子"); if(i == 5) { System.out.println("主线程(小弟)让 子线程(老大)先吃"); //join,线程插队 // b.join();//这里相当于让 t2 线程先执行完毕 Thread.yield();//礼让,不一定成功,(老大)吃的太多了,得吃到牛年马月,(小弟)不用让了, System.out.println("线程(老大)吃完了,主线程(小弟)接着吃"); } } } } class B extends Thread { @Override public void run() { for (int i = 0; i <= 20; i++) { try { Thread.sleep(1000);//休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程(老大)吃了 " + i + " 包子"); } } }
590,线程插队练习
package com.hspedu.threaduse; public class ThreadMethod02 { public static void main(String[] args) throws InterruptedException { B b = new B(); Thread thread = new Thread(b);//创建子线程 for (int i = 1; i <= 10; i++) { System.out.println("hi " + i); Thread.sleep(1000); if(i == 5) {//说明主线程输出了5次 hi thread.start();//启动子线程 输出 hello thread.join();//立即将thread子线程,插入到main线程,让thread先执行 } } System.out.println("主线程结束..."); } } class B implements Runnable { private int count = 0; @Override public void run() { while(true) { System.out.println("hello " + (++count)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(count == 10) { System.out.println("子线程结束..."); break; } } } }
591,守护线程
下面我们测试如何将一个线程设置成守护线程
package com.hspedu.threaduse; public class ThreadMethod03 { public static void main(String[] args) throws InterruptedException { MyDaemonThread myDemonThread = new MyDaemonThread(); //如果我们希望当main线程结束后,子线程自动结束 //只需将子线程设为守护线程即可 myDemonThread.setDaemon(true); myDemonThread.start(); for (int i = 1; i <= 10; i++) {//main线程,子线程是无限循环,当main线程结束后,子线程不会结束 System.out.println("宝强在辛苦的工作..."); Thread.sleep(1000); } } } class MyDaemonThread extends Thread { @Override public void run() { for (; ; ) {//无限循环 try { Thread.sleep(1000); } catch (InterruptedException e) {//休眠50毫秒 e.printStackTrace(); } System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~"); } } }
592,线程7大状态
写程序查看线程状态
package com.hspedu.threaduse; public class ThreadState { public static void main(String[] args) throws InterruptedException { //创建一个线程,马上看它的状态是啥 C c = new C(); System.out.println(c.getName() + " 状态 " + c.getState()); c.start(); //启动后,用一个循环,只要这个线程还没有终止,就不停的看它当前是啥状态 while (Thread.State.TERMINATED != c.getState()) { System.out.println(c.getName() + " 状态 " + c.getState()); Thread.sleep(1000);//让主线程休眠 } //等退出while循环后,说明它已经终止了,再看它最后的状态是啥 System.out.println(c.getName() + " 状态 " + c.getState()); } } class C 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; } } }
593,线程同步机制
在run方法中也要加入 休眠 代码,
本节代码解决售票卖出负票的问题。
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();//第 1 个线程-窗口 // new Thread(sellTicket02).start();//第 1 个线程-窗口 //测试 SellTicket03 sellTicket03 = new SellTicket03(); new Thread(sellTicket03).start();//第 1 个线程-窗口 new Thread(sellTicket03).start();//第 1 个线程-窗口 new Thread(sellTicket03).start();//第 1 个线程-窗口 } } //实现接口方式,使用 synchronized实现线程同步 class SellTicket03 implements Runnable { private int ticketNum = 100;//让多个线程共享 ticketNum private boolean loop = true;//控制run方法变量 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() {//synchronized 不能修饰 run方法,因为修饰了,就相当于进3个人只能卖给一个票,其他2个人白进了,总共排了3个队伍 //不修饰的话,相当于卖完1个人的票,再卖下一个人的,总共排了1个队伍 while (loop) { sell();//sell方法是一个同步方法 //休眠 50 毫秒, 模拟 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } //使用 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)); } } } }
594,互斥锁
package com.hspedu.syn; //使用多线程, 模拟三个窗口同时售票 100 张 public class sellTicket { public static void main(String[] args) { //测试 SellTicket03 sellTicket03 = new SellTicket03(); new Thread(sellTicket03).start();//第 1 个线程-窗口 new Thread(sellTicket03).start();//第 1 个线程-窗口 new Thread(sellTicket03).start();//第 1 个线程-窗口 } } //实现接口方式,使用 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 () {//synchronized 不能修饰 run方法,因为修饰了,就相当于进3个人只能卖给一个票,其他2个人白进了,总共排了3个队伍 //不修饰的话,相当于卖完1个人的票,再卖下一个人的,总共排了1个队伍 while (loop) { sell();//sell方法是一个同步方法 //休眠 50 毫秒, 模拟 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } } //使用 Thread 方式 // new SellTicket01().start() // new SellTicket01().start() ,new出了不同的对象,不能用互斥锁 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)); } } }
595,线程死锁和释放锁
死锁运行结果就会卡住,所以写代码一定要避免
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; } //下面业务逻辑的分析 //1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁 //2. 如果线程 A 得不到 o2 对象锁, 就会 Blocked //3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁 //4. 如果线程 B 得不到 o1 对象锁, 就会 Blocked public void run() { 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"); } } } } }
597,线程家庭作业1
思路如下图:
通知的方式可以在A线程设置一个boolean变量,B线程里因为有A对象了,通过A对象将变量设置成false,A线程就退出了
package com.hspedu.syn; import java.util.Queue; import java.util.Scanner; import java.util.concurrent.BrokenBarrierException; public class Homework01 { public static void main(String[] args) { A a = new A(); B b = new B(a);//一定要注意,先把A对象放进B里去,才能启动后控制a a.start(); b.start(); } } //创建A线程类 class A extends Thread { private boolean loop = true; public void setLoop(boolean loop) { //可以修改loop变量 this.loop = loop; } @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(); } } } } 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) 表示退出:");//输入q后,回车 char key = scanner.next().toUpperCase().charAt(0); if (key == 'Q') { //以通知的方式结束A线程 a.setLoop(false); System.out.println("B线程退出"); break; } } } }
598,线程家庭作业2
思路见下图:和售票问题有点像
package com.hspedu.syn; import java.text.BreakIterator; import java.util.Queue; import java.util.Scanner; import java.util.concurrent.BrokenBarrierException; public class Homework01 { 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,因为这里涉及到多个线程共享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) { //判断余额是否够 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(); } } } }
标签:java,Thread,void,基础,start,线程,new,public From: https://www.cnblogs.com/romantichuaner/p/18396184