Java线程和进程

java线程和进程

进程

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

线程

  • 概念:是进程中单个顺序控制流是一条执行路径
    • 单线程:一个进程只有一个执行路径(按照顺序)
    • 多线程:即多条执行路径
  • 多线程例子
    • 扫雷:一个控制时间一个控制玩家自己的顺序 卖票存取钱等。

多线程的实现方式

  • 一:继承Thread类(后面有通过接口实现)
    • 定义一个类MyThread继承Thread类
    • 在MyThread类中重写run方法(区分能被线程执行的代码)
    • 创建MyThread类的对象
    • 启动线程
    • 注意点
      • 重写run方法,因为该方法是封装线程执行代码的,直接调用它的话会被当成普通方法调用
      • 需要调用start方法启动线程,然后由JVM调用该线程的run方法
      • 获取线程名称时如果要在main中获取则用Thread.currentThread().getName(),获取名称有三种方式1、get set方法(getName直接用相当于this.getName()),2、带参构造,3、静态方法currentThread()返回对当前执行线程的引用

package Thread;

public class ThreadName extends Thread{
    ThreadName(String name){
        super(name);//超类有这个带参构造则一定要传super的参数且为第一句
    }
    //@Override
    public void run() {
        for(int i=0;i<50;i++){
            System.out.println(getName()+":"+i);
        }
    }
}



package Thread;

public class ThreadNameDemo{
    public static void main(String[] args) {
        ThreadName tn1=new ThreadName("线程1");//tn1.setName("线程1")
        ThreadName tn2=new ThreadName("线程2");
        tn1.start();
        tn2.start();
        //也可以在main函数中用getName方法,但是因为测试类没有继承Thread类,所以不可以直接用getName,需要用到Thread.currentThread().getName()
    }
}
  • 二、线程调度

    • java采用抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个
    • 但是优先级高并不一定就是一直先执行,只是该线程抢占到的CPU时间片的几率更大,但是要在次数比较多或者运行多次的时候才可以看到想要的效果
    • 线程默认值是5,优先级范围是1-10
    • 在这里插入图片描述
public class ThreadPriorityDemo {
    public static void main(String[] args) {
        ThreadPriority tp1=new ThreadPriority();
        ThreadPriority tp2=new ThreadPriority();
        tp1.setName("线程1");
        tp2.setName("线程2");
        System.out.println(tp1.getPriority());
        System.out.println(tp2.getPriority());
        tp1.setPriority(1);
        tp2.setPriority(10);
        tp1.start();
        tp2.start();
    }
}

public final int getPriority():返回线程优先级
public final void setPriority():更改此线程的优先级
  • 三、线程控制
    • 在这里插入图片描述

    • join方法可以让该线程执行完成后才开始其他线程

    • 抛InterruptedException(说明该方法是可能会花一点时间,但是是可以取消的方法)的代表方法有:

        1. java.lang.Object 类的 wait 方法
        1. java.lang.Thread 类的 sleep 方法
        1. java.lang.Thread 类的 join 方法

执行wait方法的线程,会进入等待区等待被notify/notify All。在等待期间,线程不会活动。

执行sleep方法的线程,会暂停执行参数内所设置的时间。

执行join方法的线程,会等待到指定的线程结束为止。

public class ThreadSleep extends  Thread{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println(getName()+":"+i);
            try {
                Thread.sleep(1000);//让当前正在执行的线程休眠1s(暂停执行),每次执行三者各执行一次,因为ts1抢到后执行run()休眠1次,ts2抢到后也休眠1次同时抢抢到后执行调用run才开始休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


public class ThreadJoinDemo {
    public static void main(String[] args) {
        ThreadJoin tj1=new ThreadJoin();
        ThreadJoin tj2=new ThreadJoin();
        ThreadJoin tj3=new ThreadJoin();
        tj1.setName("开花");
        tj2.setName("结果1");
        tj3.setName("结果2");
        tj1.start();
        try {
            tj1.join();//等待tj1执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tj2.start();
        tj3.start();

    }

}



public class ThreadDaemondemo {
    public static void main(String[] args) {
        ThreadDaemon td1=new ThreadDaemon();
        ThreadDaemon td2=new ThreadDaemon();
        td2.setName("士兵1");
        td1.setName("士兵2");
        //设置主线程为将军
        Thread.currentThread().setName("将军");
        //设置守护线程
        td1.setDaemon(true);
        td2.setDaemon(true);
        td1.start();
        td2.start();//守护线程在主线程结束后很快执行完毕但是不是立即结束
        for(int i=0;i<40;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

尽管主线程在程序启动时自动创建,但它可以由一个Thread对象控制。为此,你必须调用方法currentThread()获得它的一个引用,currentThread()是Thread类的公有的静态成员。

  • 三、线程的生命周期
    • 在这里插入图片描述

    • 所以之前的sleep方法是它运行一次之后阻塞了,再回去抢执行权三个线程每个都是运行一次再回去抢,所以是三个为一轮

  • 多线程的其他实现方式:(创建新执行线程有两种方法)
    • 一、将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法,接下来可以分配并启动该子类的实例
    • 二、声明实现 Runnable 接口的类。该类实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动
    • 之前的例子都是采用第一种方式实现,接下来采用第二种
    • 第二种方式避免了java单继承的局限性
      • 定义一个类Myrunnable类实现Runnable接口
      • 在MyRunnable类中重写run方法
      • 创建Myrunnable类的对象
      • 创建Thread对象将上面的对象作为参数传递给Thread对象
      • 启动线程
    • Thread构造方法
      • 在这里插入图片描述
public class MyRunnable implements Runnable{
    //Runnable接口和Thread类没有关系不能调用Thread中的getName方法
    @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 mr=new MyRunnable();
        Thread t1=new Thread(mr);//Thread t1=new Thread(mr,"线程1")
        Thread t2=new Thread(mr);//Thread t2=new Thread(mr,"线程2")起名
        t1.start();
        t2.start();
    }
}

综合案例(互斥机制锁机制)

  • 售票
//第一次:出现两种问题负数票和一票多用,问题原因是线程执行的随机性
public void run() {
        while(true){
            if(ticket>0){
                try {
                    Thread.sleep(100);//通过sleep模拟出票时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
                //可能在ticket--之前t2或者t3抢到了CPU的执行权:这就是看到同一张票出现多次的原因
                //可能在t1将ticket--变成0时,t1不能再继续售票,但t2已经进来循环了,所以还可以--:这就是出现负数票的原因
                ticket--;
            }
        }
    }

  • 因为随机性出现的问题基础:多线程环境并且有共享数据资源且有多条语句操作共享数据
  • 解决问题:将多条语句操作共享数据的代码锁起来,让任意时刻只有一个线程
    • 同步代码块:synchronized(任意对象){多条语句操作共享数据的代码块}
      (任意对象可看成是一把锁)
public class SellTicket implements Runnable{
    private int ticket=100;
    private Object obj=new Object();//共用一把锁
    @Override
    public void run() {
        while(true){
            synchronized (obj){
                //如果是t1先抢到执行权则t1执行到这里会把这段代码锁起来,t2、t3只能等即时t1休眠期直到t1出来代码块
                if(ticket>0){
                    try {
                        Thread.sleep(100);//通过sleep模拟出票时间
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
                    ticket--;
                }
            }
            }
    }
}
  • 同步好处和弊端:解决了多线程的数据安全问题但是当线程很多时,每个线程都会去判断同步上的锁,这样耗费资源降低运行效率
  • 同步方法:修饰符 sychronized 返回值类型 方法名(方法参数){}
  • 同步静态方法:修饰符 static sychronized 返回值类型 方法名(方法参数){}
    • 同步方法中锁的对象是this(如果是静态方法需要将this换成类名.class)

猜你喜欢

转载自blog.csdn.net/Phoebe4/article/details/110749734