Java多线程入门

进程与线程

进程:进程是程序的基本执行实体,比如你现在正在看的CSDN就是一个进程。

线程:线程是操作系统可以进行运算调度的的最小单位,被包含在进程之中,是进程中的实际运算单位。其实就是一个进程中相互独立,可以同时运行的功能。

多线程

见名知意,多线程就是让一个进程有多个线程。

作用:多线程可以让一个程序同时做多件事情,进而提高效率

多线程的实现方式

第一种:

1.自己定义一个类继承Thread类(线程)
——Thread类就表示线程类
2.重写run方法(方法里面写要执行的代码)
3.创建子类对象,并启动线程(对象.start)
——可以给线程命名:线程.setName();
——这样要用的时候,就可以用getName获取线程名字了

第二种:

1.自己定义一个类实现Runnable接口

2.重写run方法

3.创建自己类(就是实现类)的对象

4.创建Thread类的对象,把自己类的对象传入构建方法中的小括号中

5.用Thread类的对象.start(),也可以给Thread对象起名字来区分

——但是这样怎么才能获取到线程名字呢?

——要在run方法里先获取当前线程:

Thread.currentThread();//这是Thread类里面的静态方法,返回当前执行这个run方法的线程对象

——之后我们可以用这个对象再调用getName方法获取名字

第三种:

1.创建一个类实现Callable接口,这个接口有个泛型,表示返回值的类型

2.重写call方法,这个方法是有返回值的,返回值是Callable接口的泛型,表示多线程运行的结果,方法体写要执行的代码

3.在main类里面创建这个实现类的对象1,表示多线程要执行的任务

4.创建Future实现类FutureTask类的对象2泛型也写返回值的结果管理多线程运行的结果,把对象1放进这个类的构造方法中,进而管理运行的结果

5.创建Thread类对象,把对象2放进构造方法,启动start,这样就启动了线程

6.对象2.get();返回运行的结果

三种方式区别

——第一种和第二种无返回值,第三种有返回值

——第一种扩展性差,不能再继承别的类;第二种第三种扩展性强

Thread类常用方法

String getName()  

返回当前线程的名字。如果我们没给线程设置名字,那也是有默认名字的(是在构造方法中自己默认的),格式为Thread—X(X是序号,从0开始)

void setName(String name)

设置线程名字,构造方法也可以设置名字。我们想设置名字,可以用这个方法,也可以用构造方法(Thread类的继承类要重写父类Thread的构造方法,这个构造方法的参数是String name)

static Thread currentThread()

获取当前线程的对象。当JVM虚拟机启动,会有一个叫做main的线程执行main方法

static void sleep(long time)

让线程休眠指定时间,单位为毫秒。哪条线程执行到这个方法,哪条就会停留对应的时间;方法的参数表示休眠的时间,单位是毫秒;当时间到了,线程醒来,继续执行下面代码;有异常,父类中方法不能抛的,子类也不可以抛出,Thread类中run方法没有抛出异常,那么作为重写方法,自然也不能抛出

线程的优先级

先了解调度

——抢占式调度:多个线程在抢夺CPU执行权,体现了随机性

——非抢占式调度:多个线程有顺序的掌控CPU执行权,时间也差不多

在Java中,线程采取的是抢占式调度。

优先级越大,抢占到CPU执行权的概率越大,共有十档,最小是1,最大是10,不设置优先级的话,默认为5。

守护线程

当其他非守护线程结束的时候,守护线程也会陆续结束 。

出让/礼让线程

让线程把CPU执行权出让,但也有可能自己再次抢到

插入/插队线程

作用是可以等插入线程执行完毕,在执行之后的线程

假设在main方法中,有一个线程t,调用t.join();

//会先把t线程执行完,再执行main线程;

//表示把t这个线程插入到当前线程前面

线程安全

原因:线程在执行的时候,具有随机性。

当我们的线程在运行的时候,有些操作不是原子操作,会出现在运行的时候被其他线程打断,这样有时就会造成数据错误的问题,也就是线程安全的问题

那么怎么处理这些影响线程安全的代码呢?

1.同步代码块:

把操作共享数据的代码锁起来;

——格式:

synchronized(锁){

操作共享数据的代码;

}

——特点:

1.锁默认打开,有一个线程进去,锁自动关闭

2.里面的代码执行完毕,线程出来,锁自动打开

3.锁一定是唯一的,共同的;我们经常用类.class一个类的字节码文件)当锁,因为一个包中一个类的字节码文件一定是唯一的。

——线程的一般写法:

1.循环
2.同步代码块
3.判断共享数据到没到末尾
4.如果没到末尾,执行逻辑
5.如果到了末尾,退出循环

2.同步方法:

把整个方法内所有代码都锁起来;

就是把synchronized关键字加到方法上

——格式:

修饰符 synchronized 返回值 方法名(){}

——特点:

1.锁住所有代码

2.锁对象不能自己指定,是Java规定好的

——非静态:this

——静态:当前类的字节码文件对象

不知道怎么写?可以先写同步代码块,然后选中ctrl+alt+m变成方法。

3.Lock锁:

用实例对象.lock()为上锁;用实例对象.unlock()为手动释放锁;

而且这个实例对象也要是唯一的。

小细节:

try{}catch(){}finally{}

//可以把实例对象.unlock()放到finally里面,因为这个异常处理体系中,finally无论如何都会被执行

生产消费模型/等待唤醒机制

生产者:生产数据

消费者:消费数据

核心思想:而且要有第三方来控制线程执行

下面的方法都是锁对象调用的:

自己实现

public class Desk {
    //控制线程的执行
    //1有,0没有
    public static int food = 0;
    //数据最多的个数
    public static int count=10;
    //锁对象
    public static Object lock = new Object();

}
public class Cook extends Thread{
    @Override
    public void run() {
    /*
    1.循环
    2.同步代码块
    3.判断共享数据到没到末尾
    4.如果没到末尾,执行逻辑
    5.如果到了末尾,退出循环
     */
        while (true){
            synchronized(Desk.lock){
                if (Desk.count==0){
                    break;
                }else {
                    if(Desk.food==0){
                        System.out.println("厨师正在做包子");
                        Desk.food=1;
                        Desk.lock.notify();
                    }else {
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }

    }
}
public class People extends Thread{
  public void run(){
    /*
1.循环
2.同步代码块
3.判断共享数据到没到末尾
4.如果没到末尾,执行逻辑
5.如果到了末尾,退出循环
*/
    while (true){
      synchronized(Desk.lock){
        if (Desk.count==0){
          break;
        }else {
          if(Desk.food==0){
            try {
              Desk.lock.wait(); //让当前线程跟锁对象产生联系,
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }else {
            Desk.count--;
            System.out.println("人正在吃包子,还能吃"+(Desk.count)+"个包子");

            //唤醒厨师
            Desk.lock.notify();
            Desk.food=0;
          }
        }
      }
    }
  }
  
}

 测试类:

public class Test {
    public static void main(String[] args) {
        Cook cook=new Cook();
        People people=new People();
        cook.setName("厨师");
        people.setName("顾客");
        cook.start();
        people.start();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值