0基础学java-day17-(多线程基础)

一、线程

1 线程相关概念

1.1 程序(program)

1.2 进程 

1.3 什么是线程 

1.4 其他相关概念

package com.hspedu;

/**
 * @author 林然
 * @version 1.0
 */
public class CpuNum {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        //获取当前电脑CPU数量
        int cpuNums = runtime.availableProcessors();
        System.out.println(cpuNums);
    }
}

2 线程基本使用

2.1 创建线程的两种方式

2.2 线程应用案例 1-继承 Thread  

【主线程结束了不代表其他线程结束了】 

package com.hspedu.threaduse;

/**
 * @author 林然
 * @version 1.0
 * 演示通过继承 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() {//写上自己的业务逻辑
        super.run();
        //该线程每隔 1 秒。在控制台输出 “喵喵, 我是小猫咪”
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println("喵喵, 我是小猫咪" +(++times)+"线程名"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(times==80)
            {
                break;//当 times 到 80, 退出 while, 这时线程也就退出
            }
        }


    }
}

 

 2.3 线程应用案例 2-实现 Runnable 接口

package com.hspedu.threaduse;

/**
 * @author 林然
 * @version 1.0
 * 通过实现接口 Runnable 来开发线程
 */
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
        //Proxy threadProxy = new Proxy(tiger);
        //Proxy.start();
    }
}
class Animal {
}
class Tiger extends Animal implements Runnable {
    @Override
    public void run() {
        System.out.println("老虎嗷嗷叫....");
    }
}
//建议的线程模拟
//线程代理类 , 模拟了一个极简的 Thread 类
class Proxy implements Runnable{//你可以把 Proxy 类当做 ThreadProxy(代理类)
    private Runnable target =null;//属性,类型是 Runnable
    @Override
    public void run() {
        if(target!=null)
        {
            target.run();
        }
    }

    public Proxy(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());
            //休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count==10)
            {
                break;
            }
        }
    }
}

 2.4 线程使用应用案例-多线程执行

package com.hspedu.threaduse;

/**
 * @author 林然
 * @version 1.0
 */
public class Thread03 {
    public static void main(String[] args) {
        T1 t1 =new T1();//启动第 1 个线程
        T2 t2 =new T2();//启动第 2 个线程
        Thread thread =new Thread(t1);
        Thread thread1 = new Thread(t2);
        thread.start();
        thread1.start();
    }
}
class T1 implements Runnable {
    int times = 0;

    @Override
    public void run() {
        while (true) {
            //每隔 1 秒输出 “hi”,输出 5 次
            System.out.println("hello,world " + (++times));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (times == 5) {
                break;

            }
        }
    }
}
class T2 implements Runnable {
    int times = 0;

    @Override
    public void run() {
        while (true) {
            //每隔 1 秒输出 “hello,world”,输出 10 次
            System.out.println("hi " + (++times));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (times == 10) {
                break;

            }
        }
    }
}

 2.5 线程如何理解

3 继承 Thread vs 实现 Runnable 的区别 

package com.hspedu.ticket;

/**
 * @author 林然
 * @version 1.0
 * 使用多线程,模拟三个窗口同时售票 100 张
 */
public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01=new SellTicket01();
//        SellTicket01 sellTicket011=new SellTicket01();
//        SellTicket01 sellTicket012=new SellTicket01();
//        //这里我们会出现超卖..
//        sellTicket01.start();//启动售票线程
//        sellTicket011.start();//启动售票线程
//        sellTicket012.start();//启动售票线程
        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){
            ticketnum=ticketnum-1;
            if(ticketnum<=0){
                System.out.println("售票结束");
                break;
            }
            //休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketnum));
        }
    }
}

4 线程终止

1 基本说明

.4.2 应用案例 

package com.hspedu.exit_;
/**
 * @author 林然
 * @version 1.0
 */
public class Thread_Exit_   {

    public static void main(String[] args) throws InterruptedException{
         T t = new T();
         t.start();
         //如果希望主线程去控制t1,就是可以修改变量就可以了
        //让t1退出run方法
        //休眠10秒再通知终止
        Thread.sleep(1000*10);
        t.setLoop(false);
    }
}
class T extends Thread{
    int count =0;
    //设置一个控制变量
    private boolean loop=true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        while (loop){
            //休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("T 运行中"+(++count));
        }
    }
}

 二、线程常用方法

1 常用方法第一组

2 注意事项和细节 

3 应用案例 

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);
        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(5000);//20秒
            } catch (InterruptedException e) {
                //当该线程执行到一个interrupt 方法时,就会catch 一个 异常, 可以加入自己的业务代码
                //InterruptedException 是捕获到一个中断异常.
                System.out.println(Thread.currentThread().getName() + "被 interrupt了");
            }
        }
    }
}

 4 常用方法第二组

5 应用案例 

测试 yield join 方法 ,注意体会方法的特点,
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 +  " 包子");
        }
    }
}

 6 课堂练习

package com.hspedu.method;

public class ThreadMethodExercise {
    public static void main(String[] args) throws InterruptedException {
        Thread t3 = new Thread(new T3());//创建子线程
        for (int i = 1; i <= 10; i++) {
            System.out.println("hi " + i);
            if(i == 5) {//说明主线程输出了5次 hi
                t3.start();//启动子线程 输出 hello...
                t3.join();//立即将t3子线程,插入到main线程,让t3先执行
            }
            Thread.sleep(1000);//输出一次 hi, 让main线程也休眠1s
        }
    }
}

class T3 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) {
                break;
            }
        }
    }
}

 7 用户线程和守护线程

8 应用案例 

下面我们测试如何将一个线程设置成守护线程【将main设置为用户进程,T1为守护进程】 

【先设置为保护线程,再启动,注意顺序】

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("马蓉和宋喆快乐聊天,哈哈哈~~~");
        }
    }
}

 三、线程的生命周期

1 JDK 中用 Thread.State 枚举表示了线程的几种状态

2 线程状态转换图 

【如果将Runable状态细分为两个状态,那么就是所谓的七个状态】        

3 写程序查看线程状态 

package com.hspedu.state_;

/**
 * @author 林然
 * @version 1.0
 */
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()  {
        for (int i = 0; i < 10; i++) {
            System.out.println("hi"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 四、线程的同步

1 先看一个问题

2.Synchronized 

2.1 线程同步机制

2 .2同步具体方法-Synchronized 

package com.hspedu.syn;

/**
 * @author 林然
 * @version 1.0
 */
public class SellTicket {

    public static void main(String[] args) {
        //测试一把
        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方法变量
    //1. public synchronized void sell() {} 就是一个同步方法
    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() {
        while (loop) {

            sell();//sell方法是一个同步方法
        }
    }
}

3.分析同步原理

五、互斥锁 

1 基本介绍

【一个对象对应一把锁,所以我们应对同一个对象进行上锁】

2 使用互斥锁来解决售票问题 

代码块加锁,和方法上加锁
package com.hspedu.syn;

/**
 * @author 林然
 * @version 1.0
 */
public class SellTicket {

    public static void main(String[] args) {
        //测试一把
        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
    public synchronized static void m1() {
    }
    //2. 如果在静态方法中,实现一个同步代码块.
    /*
    synchronized (SellTicket03.class) {
        System.out.println("m2");
    }
*/
    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() {
        while (loop) {

            sell();//sell方法是一个同步方法
        }
    }
}

3.注意事项和细节

六、线程的死锁 

.1 基本介绍

2 应用案例 

3 应用案例 

package com.hspedu.syn;

/**
 * @author 林然
 * @version 1.0
 */
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");
                }
            }
        }
    }
}

4.释放锁

4.1下面操作会释放锁 

4.2 下面操作不会释放锁 

七、本章作业 

1.作业一

package com.hspedu.homework;

import java.util.Scanner;

/**
 * @author 林然
 * @version 1.0
 */
public class Homework01 {
    public static void main(String[] args) {
        A a =new A();
        a.start();
        B b = new B(a);//一定要注意.
        b.start();
    }
}
class A extends Thread{
    private boolean loop=true;
    @Override
    public void run() {
        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;
            }
        }
    }
}

2.作业二 

package com.hspedu.homework;

/**
 * @author 林然
 * @version 1.0
 */
public class Homework02 {
    public static void main(String[] args) {
        T t =new T();
        new Thread(t).start();
        new Thread(t).start();
    }
}
//编程取款的线程
//1.因为这里涉及到多个线程共享资源,所以我们使用实现Runnable方式
//2. 每次取出 1000
class T implements Runnable{
    private static int money = 10000;
    @Override
    public void run() {
        while(true){
            synchronized (this){
                if(money>=1000){
                    money-=1000;
                    System.out.println("取出1000"+"余额:"+money);

                }
                else {
                    System.out.println("没钱了"+"余额:"+money);
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜菜小林然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值