JAVA学习笔记之线程

一、并发和并行

并行:两个或多个任务在同一时间点执行

并发:两个或多个任务在同一个时间段执行

单核CPU只能实现并发

二、进程和线程

进程:
正在运行中的程序;
作用:用来封装线程,为线程提供资源(比如:内存资源)

线程:
线程是进程中独立的执行单元
作用:来执行独立的任务(执行代码)

三、线程的创建

创建步骤:

  1. 创建子类集成Thread类
  2. 重写Thread类中的run方法

使用步骤:

  1. new一个上面写好的对象
  2. 调用start方法(线程会自动调用run方法)
public class test {
    public static void main(String[] args) {

        one one = new one();
        one.start();
    }
}


public class one extends Thread{

    @Override
    public void run() {
        System.out.println("heiheihei");
    }
}

1.主线程和子线程

主线程:

程序启动时内部自动创建执行main方法的线程称为主线程

子线程:

除了主线程以外的所有线程被称为子线程

区分:

根据线程名称来判断,主线程名称是main,并且执行main方法

子线程默认名字是:Thread-序号

2.线程的运行模式和原理

2.1 分时式模式:

所有线程轮流获得CPU的使用权,并且平均分配给每个线程使用

2.2 抢占式模式:

优先级较高的线程先获得CPU的概率更高,谁抢到谁执行,但不一定会执行完,就会再次被抢走,执行别的线程代码;

Java中的线程就属于这个模式。

注意要点:

  1. 在同一个线程中,代码是从上往下执行的
  2. 每个线程都会有自己的独立栈空间
  3. 所有线程共享进程的堆空间

3.Thread常用方法

public String getName();

获取线程名

public void setName();

设置线程名

pulbic static void sleep(long millis);

让当前线程休眠,单位:毫秒

public static Thread currentThread();

获取当前线程对象

public class test {
    public static void main(String[] args) {

        one one = new one();
        one.start();

        System.out.println(one.getName());
        one.setName("123456");
        System.out.println(one.getName());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());

        for (int i = 0; i <10 ; i++) {
            System.out.println("main"+"\t"+i);
        }
    }
}


public class one extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            System.out.println("run"+"\t"+i);
        }
    }
}

4.线程的创建方式

实现Runnable接口:

步骤:

  1. 创建类实现runnable接口
  2. 实现run方法
  3. 创建Thread类,封装实现了runnable接口的对象
  4. 调用Thread对象的start方法
public class test {
    public static void main(String[] args) {
        Thread th = new Thread(new two());
        th.start();
    }
}


public class two implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnalble接口");
    }
}

继承Thread:

public class test {
    public static void main(String[] args) {
        one one = new one();
        one.start();
    }
}


public class one extends Thread{
    @Override
    public void run() {
        System.out.println("heiheihei");
    }
}

实现Runnable接口的好处:

  1. 解除Java类单继承的局限性
  2. 可以更好的在多个线程之间共享数据
  3. 为线程池提供前提条件

匿名内部类创建线程:

匿名内部类:

​ 作用:

​ 1.直接创建已知类的子类对象

​ 2.直接创建已知接口的实现类对象

​ 语法:

​ 1.直接创造已知类的子类对象:

​ new 类名 (){

​ }

​ 2.直接创造已知接口的实现类对象:

​ new 接口名 (){

​ }

代码演示:

public class test {
    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        };
        t.start();
    }
}
public class test {
    public static void main(String[] args) {
        Runnable run = new Runnable() {
            @Override
            public void run() {
                //这样产生的不属于Thread的子类,所以需要使用
                //Thread.currentThread()获取调用本方法的线程
                System.out.println("匿名内部类,接口方式");
            }
        };
        Thread thread = new Thread(run);
        thread.start();
    }
}

5.线程安全

线程安全的概念:

指两个或多个线程操作同一个共享资源的时候,仍然能够得到正确的结果则称为线程安全

实现线程安全的方法:

线程同步:让多个线程按顺序执行

实现线程同步的方式:

1.同步代码块

2.同步方法

3.Lock接口

缺点:

由于同一个时间只能有一个线程操作代码,所以线程安全会导致效率低下

5.1 同步代码块

格式:

synchronized(锁对象){
    //在这里写代码块
}

效果:

可以保证同一时间只能有一个线程操作代码块中的代码,但是其中一般只存放操作共享资源的代码块

注意事项:

任意类型的对象都可以充当锁对象;

锁对象必须被所有线程共享(共有一把锁);

代码

public class test {
    public static void main(String[] args) {
        SaleCollection collection = new SaleCollection();
        Thread th = new Thread(collection);
        Thread thread = new Thread(collection);
        th.start();
        thread.start();
    }
}


public class SaleCollection implements Runnable {

    private static int Collection = 10;
    Object object = new Object();

    @Override
    public void run() {
        while (true) {
            //这里也可以用成员变量 Object,只要是共享的且只有一个的对象都可以
            //但是为了简便和,this完全可以胜任
            synchronized (this) {
                if (Collection > 0) {
                    System.out.println(Thread.currentThread().getName() +
                            "卖了一张票,还剩" + (--Collection));
                    continue;
                }
            }
            System.out.println("票卖完了!");
            break;
        }
    }
}

5.2 同步方法

格式:

修饰符 synchronized 返回值类型 方法名 (参数列表){
    
}

效果同上面一样,

能够保证同一时间只有一个线程可以操作方法体中的代码

注意事项:

同步方法的锁对象其实也是有的,只不过都是默认的,而且无法修改。

非静态同步方法的锁对象是this

静态同步方法的锁对象是类名.class

其实同步方法和同步代码块没有太大的区别:

只是同步方法把需要同步的代码块单独拿出来写了一个方法

代码:

public class test {
    public static void main(String[] args) {
        SaleCollection collection = new SaleCollection();
        Thread th = new Thread(collection);
        Thread thread = new Thread(collection);
        th.start();
        thread.start();
    }
}

public class SaleCollection implements Runnable {

    private static int Collection = 10;

    @Override
    public void run() {
        while (Collection > 0) {
            sale();
        }
        System.out.println("票卖完了!");
    }

    public synchronized void sale() {
        if (Collection > 0) {
            System.out.println(Thread.currentThread().getName() +
                    "卖了一张票,还剩" + (--Collection));
        }
    }
}

5.3 Lock接口

常用方法:

void lock();//获取锁
void unlock();//释放锁

Lock接口常用实现类:ReentrantLock互斥锁

注意事项:

获取锁和释放锁的方法必须成对出现

格式:

public class ok{
    private static int cake=100;
    private Lock lock=new ReentrantLock();
    
    public run(){
        
        lock.lock();
        //操作共享资源的代码
        lock.unlock():
        
    }
}

代码:

public class test {
    public static void main(String[] args) {
        SaleCollection collection = new SaleCollection();
        Thread th = new Thread(collection);
        Thread thread = new Thread(collection);
        th.start();
        thread.start();
    }
}

public class SaleCollection implements Runnable {

    private static int Collection = 100;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {

        while (Collection > 0) {
            lock.lock();
            if (Collection > 0) {
                System.out.println(Thread.currentThread().getName() +
                        "卖了一张票,还剩" + (--Collection));
            }
            lock.unlock();
        }
        System.out.println("票卖完了!");
    }
}

6.线程等待和唤醒

方法:

void wait()//等待,让线程释放CPU的使用权,进入无限等待状态,需要替他线程调用 notify() 唤醒
    
void notify()//唤醒,随机唤醒一个正在等待的线程,让线程进入可执行状态

使用注意事项:

1.必须由锁对象调用

2.必须在同步方法或同步代码块中调用

代码:

public class test {
    public static void main(String[] args) {
        //容器,用来存放包子
        ArrayList<String> list = new ArrayList<>();
        //把容器传入线程,共给使用和相互唤醒
        Thread bao = new Thread(new baozi(list));
        Thread chi = new Thread(new chihuo(list));
        bao.start();
        chi.start();
    }
}

public class chihuo implements Runnable {
    //存放包子的容器
    private ArrayList list;
    //接受包子容器
    public chihuo(ArrayList list) {
        this.list = list;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (list){
                //包子容器有了包子
                if (list.size() == 1) {
                    //吃货吃掉包子
                    list.remove(0);
                    //打印信息
                    System.out.println("吃货吃完包子了,店主快点做包子");
                    //唤醒另一个携带包子容器的线程
                    //因为只有一个在等待的线程,所以刚好可以唤醒
                    list.notify();
                }else {
                    try {
                        //做好包子了或者包子包子还在
                        //那么就让这个线程等待
                        list.wait();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

public class baozi implements Runnable {
    //存放包子的容器
    private ArrayList list;
    //接受包子容器
    public baozi(ArrayList list) {
        this.list = list;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (list) {
                if (list.size() == 0) {
                    list.add("baozi");
                    System.out.println("店主做好包子了,等待吃货来吃");
                    list.notify();
                } else {
                    try {
                        list.wait();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

7.线程的状态(生命周期)

NEW : 新建状态,刚刚创建好,还没有调用start方法;

RUNNABLE: 可运行状态,只有在这种状态下,线程才能去抢占CPU资源,抢到就执行,否则就一直抢

BLOCKED: 阻塞状态,没有获得锁对象

WAITING: 无限等待状态,调用wait()方法后进入,需要其他线程唤醒

TIMED_WAITING: 计时等待状态,调用sleep方法或者wait(long time)进入的状态

TERMINATED: 死亡状态,任务正常结束或者调用了stop方法进入的状态

8.Sleep和wait的区别

sleep在进入睡眠的时候,不会释放锁。

wait在进入等待状态之后,会释放锁。

而线程想要执行,要先获得锁对象,然后才能去抢夺CPU资源执行;

线程池在下篇,已经写好:JAVA学习笔记之线程拓展 - 线程池

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值