Java基础复习_day18_多线程

多线程

一. 进程和线程

1.进程

  • 是正在运行的程序
  • 是系统进行资源分配和调用的独立单位
  • 每一个进程都有它自己的内存空间和系统资源

2. 线程

  • 是进程中的单个顺序控制流,是一条执行路径
  • 单线程:一个进程如果只有一条执行路径,则称为单线程程序
  • 多线程:一个进程如果有多条执行路径,则称为多线程程序

二. 实现多线程的方式

1. 继承Thread类

① 实现步骤

  • 定义一个类MyThread继承Thread类
  • 在MyThread类中重写run()方法
  • 创建MyThread类的对象
  • 启动线程

② 方法介绍

  • void run()
    在线程开启后,此方法将被调用执行
  • void start()
    使此线程开始执行,Java虚拟机会调用run方法()
  • void setName(String name)
    将此线程的名称更改为等于参数name
  • String getName()
    返回此线程的名称
  • Thread currentThread()
    返回对当前正在执行的线程对象的引用

③ 注意

  • 为什么要重写run()方法?

      因为run()是用来封装被线程执行的代码
    
  • run()方法和start()方法的区别?

      run():封装线程执行的代码,直接调用,相当于普通方法的调用
    
      start():启动线程;然后由JVM调用此线程的run()方法
    

④ 代码示例

class ThreadDemo extends Thread {
    @Override
    public void run() {
        System.out.println(currentThread().getName() + "启动了");
    }
}
public class MyThread {
    public static void main(String[] args) {
        ThreadDemo td1 = new ThreadDemo();
        td1.setName("线程一");
        td1.start();

        System.out.println(Thread.currentThread().getName() + "启动了");

        ThreadDemo td2 = new ThreadDemo();
        td2.setName("线程二");
        td2.start();
    }
}

2. 线程优先级

① 线程调度

  • 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
  • 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些

② Java使用的是抢占式调度模型

③ 随机性

  • 假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的

④ 优先级相关方法

  • final int getPriority()
    返回此线程的优先级
  • final void setPriority(int newPriority)
    更改此线程的优先级; 线程默认优先级是5;线程优先级的范围是:1-10
class ThreadPriority extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + ":" + i);
        }
    }
}
public class ThreadPriorityDemo {
    public static void main(String[] args) {
        ThreadPriority tp1 = new ThreadPriority();
        ThreadPriority tp2 = new ThreadPriority();
        ThreadPriority tp3 = new ThreadPriority();

        tp1.setName("高铁");
        tp2.setName("飞机");
        tp3.setName("汽车");

        //public final int getPriority():返回此线程的优先级
        System.out.println(tp1.getPriority()); //5
        System.out.println(tp2.getPriority()); //5
        System.out.println(tp3.getPriority()); //5

        //public final void setPriority(int newPriority):更改此线程的优先级
//        tp1.setPriority(10000); //IllegalArgumentException
        System.out.println(Thread.MAX_PRIORITY); //10
        System.out.println(Thread.MIN_PRIORITY); //1
        System.out.println(Thread.NORM_PRIORITY); //5

        //设置正确的优先级
        tp1.setPriority(Thread.NORM_PRIORITY); // 5
        tp2.setPriority(Thread.MAX_PRIORITY); // 10
        tp3.setPriority(Thread.MIN_PRIORITY);// 1

        tp1.start();
        tp2.start();
        tp3.start();
    }
}

⑤ 线程控制

  • static void sleep(long millis)
    使当前正在执行的线程停留(暂停执行)指定的毫秒数
  • void join()
    等待这个线程死亡
  • void setDaemon(boolean on)
    将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出

⑥ 线程的生命周期

  • 线程一共有五种状态,线程在各种状态之间转换。
    在这里插入图片描述

3. 实现Runnable接口

① 实现步骤

  • 定义一个类MyRunnable实现Runnable接口
  • 在MyRunnable类中重写run()方法
  • 创建MyRunnable类的对象
  • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
  • 启动线程
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0; i<100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public class MyRunnableDemo {
    public static void main(String[] args) {
        //创建MyRunnable类的对象
        MyRunnable my = new MyRunnable();

        //创建Thread类的对象,把MyRunnable对象作为构造方法的参数
        //Thread(Runnable target)
//        Thread t1 = new Thread(my);
//        Thread t2 = new Thread(my);
        //Thread(Runnable target, String name)
        Thread t1 = new Thread(my,"高铁");
        Thread t2 = new Thread(my,"飞机");

        //启动线程
        t1.start();
        t2.start();
    }
}

② 实现Runnable接口的好处

  • 避免了Java单继承的局限性
  • 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想

4. 线程同步

① 买票案例

  • 某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
class SellTicket implements Runnable {
    private int tickets = 100;

    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "售出了第" + tickets + "张票");
                tickets--;
            }

        }

    }
}

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();

        Thread w1 = new Thread(st, "窗口一");
        Thread w2 = new Thread(st, "窗口二");
        Thread w3 = new Thread(st, "窗口三");

        w1.start();
        w2.start();
        w3.start();
    }
}

② 卖票出现了问题

- 相同的票出现了多次
- 出现了负数的票

③ 同步代码块解决

  • 格式
synchronized(任意对象) { 
	多条语句操作共享数据的代码 
}
  • 代码演示
public class SellTicket implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            //tickets = 100;
            //t1,t2,t3
            //假设t1抢到了CPU的执行权
            //假设t2抢到了CPU的执行权
            synchronized (obj) {
                //t1进来后,就会把这段代码给锁起来
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                        //t1休息100毫秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //窗口1正在出售第100张票
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                    tickets--; //tickets = 99;
                }
            }
            //t1出来了,这段代码的锁就被释放了
        }
    }
}

④ 同步方法

  • 就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数) { 
	方法体;
}
  • 同步方法的锁对象是 this

⑤ 静态同步方法

  • 就是把synchronized关键字加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数) { 
	方法体;
}
  • 同步静态方法的锁对象是 类名.class
public class SellTicket implements Runnable {
    private static int tickets = 100;
    private int x = 0;

    @Override
    public void run() {
        while (true) {
			sellTicket()}
    }
//    同步方法
//    private synchronized void sellTicket() {
//        if (tickets > 0) {
//            try {
//                Thread.sleep(100);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
//            tickets--;
//        }
//    }
    
//  静态同步方法
    private static synchronized void sellTicket() {
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
            tickets--;
        }
    }
}

⑥ Lock锁

  • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

  • 加锁解锁方法
    void lock() 获得锁
    void unlock() 释放锁

public class SellTicket implements Runnable {
    private int tickets = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                    tickets--;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
发布了22 篇原创文章 · 获赞 4 · 访问量 1273

猜你喜欢

转载自blog.csdn.net/weixin_42931689/article/details/104298390