java基础 -线程(基础)的 笔记

 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(); } } } }

 

 

 











 

请登录后发表评论

    没有回复内容