线程简介,线程创建的三种方式,线程同步(通信),死锁

一、线程简介

1.1 进程与线程

  1. 运行在内存中的程序就是进程
  2. 内存通向cpu的执行的路径就是线程
  3. 单线程:只有一条通向cpu的执行路径就是单线程
  4. 多线程:多条通向cpu的执行的路径就是多线程
  5. 进程与线程的关系:一个进程中可以包含多个线程、一个线程只能在一个进程中
  6. 单线程在这里插入图片描述
  7. 多线程在这里插入图片描述

1.2 并发与并行

  1. 并发(高并发):在同一个时间段,执行两个或多个程序在单核cpu中交互执行,因为交换速度过快,可能产生误区是同时执行,其实是交互执行
  2. 并行:在同一个时间段,执行两个或者多个程序在多核cpu中同时执行(效率高)
  3. main方法是主线程,只有一条执行路径

1.3 线程调度的方式

  1. 线程调度的方式分为:分配式调度和抢占式调度
  2. 分配式调度:多个线程执行任务的时间都是平均分配,每个线程执行的周期都是相同
  3. 抢占式调度:线程的优先级越高,获取cpu的执行权就越高,抢到cpu的执行权,就优先执行
  4. 在这里插入图片描述

1.4 线程创建时内存分配

  1. 在这里插入图片描述
  2. 继承一个Thread类,含有run()方法。当我们调用start()方法时,jvm会自动调用run()方法,此时会在内存中开辟一个新的栈内存,加载run()方法
  3. 但如果我们在main()中直接调取run()方法,则只是将run()加载到main(),所以获取线程名就为main的线程名
  4. 总结:线程开启,都会新开辟一个栈内存,在自己的栈内存中执行run()方法(每new一个Thread类或其子类,都会在内存中开辟)

1.5 线程中的方法

  1. 设置名称和获取线程名
package qf22020308_thread.Demo02;
/**
 * 获取方法名称 两种方法:
 * 设置方法名称 两种方法:
 */
public class Demo01 {
    public static void main(String[] args) {
        //设置方法名1
        MyThread01 myThread01 = new MyThread01("花生");
        //设置方法名2
        myThread01.setName("你好");
        //获取线程名1
        System.out.println(myThread01.getName());
        //获取线程名2
        System.out.println(Thread.currentThread().getName());
        myThread01.start();
    }
}

  1. 线程的睡眠
package qf22020308_thread.Demo02;

/**
 1. 倒计时
 */
public class Demo02 {
    public static void main(String[] args) {
        for (int i = 60; i > 0; i--) {
            try {
                Thread.sleep(1000);
                System.out.println(i + " s");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

1.6 守护线程和设置线程的优先级

  1. 守护线程:A.守护其它线程执行完操作 B.一般守护线程都是守护主线程
  2. 守护线程的死亡: 被守护的线程执行完操作,或者是死亡,那么守护线程也就是死亡
  3. 优先级注意点:
    A.线程的最高优先级是10 线程的最低优先级是1 范围 1-10
    B.线程优先级越高 表示获取cpu执行权越大 并不一定会执行 java 抢占式调度

二、线程创建的三种方式

2.1 继承Thread

  1. 定义类继承Thread
package qf22020308_thread.Demo03;

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}

  1. 测试类
package qf22020308_thread.Demo03;

/**
 * 设置线程优先级
 */
public class Demo01 {
    public static void main(String[] args) {
        //实例化第一个线程
        MyThread mt1 = new MyThread();
        mt1.setPriority(8);
        mt1.start();   //启动线程
        //实例化第二个线程
        MyThread mt2 = new MyThread();
        mt2.setPriority(Thread.MAX_PRIORITY);
        mt2.start();
    }
}

2.2 实现Runnable接口

  1. 自定义类实现接口
package qf22020308_runnable.Demo01;

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}

  1. 、测试类
package qf22020308_runnable.Demo01;

public class Demo01 {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        // public Thread(Runnable target){};
        Thread th = new Thread(mr);
        th.start();//因为start()只有创建Thread才能调用
    }
}

2.3 上述两种简化-匿名内部类

  1. 使用匿名内部类创建两种方式
package qf22020308_runnable.Demo02;

public class Demo01 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName() + " " + i);
                }
            }
        }).start();

        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName() + " " + i);
                }
            }
        }.start();
    }
}

2.4 实现Callable接口-有返回值

  1. 有返回值的线程操作
  2. 自定义实现类
package qf22020309_callable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {

    int temp = 100;

    @Override
    public Integer call() throws Exception {
        return getSum(temp);
    }

    public static Integer getSum(int temp) {
        if (temp == 1) {
            return 1;
        }
        return temp + getSum(temp - 1);
    }
}
  1. 测试类
package qf22020309_callable;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //自定义类MyCallable实现Callable接口
        MyCallable mc = new MyCallable();
        //获取任务对象
        FutureTask<Integer> ft = new FutureTask<>(mc);
        //将任务对象加入Thread线程中,启动线程
        Thread t = new Thread(ft);
        t.start();
        //任务对象指针获取值
        System.out.println(ft.get());
    }
}

三、线程同步(通信)

3.1 产生问题的原因

  1. 例子:如果由a、b 线程一起执行一个1减去1的操作,如果a和b线程同时抢到cpu的执行权,抢到的都是减1的操作,那么就会出现-1的情况
  2. 如何解决问题:先让一个线程执行完操作,另一个线程才能执行操作

3.2 第一种解决方式-同步代码块

  1. synchronized (任意一个对象){问题代码}
  2. 锁的对象可以是任意的对象(可以自行定义),但必须是同一个对象
  3. 自定义类实现Runnable
package qf22020308_synchronized.Demo01;

public class MyRunnable implements Runnable {
    Object obj = new Object();
    public int n = 100;

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //对象锁 可以自定义、this、类名.class
            synchronized (obj) {
                if (n > 0) {
                    System.out.println(Thread.currentThread().getName() + " " + "剩余票数:" + n);
                    n--;
                } else {
                    break;
                }
            }
        }
    }
}

  1. 测试类
package qf22020308_synchronized.Demo01;

/**
 * 对象锁,定义一个任意的对象或者本身this,来对它上锁
 */
public class Demo01 {
    public static void main(String[] args) {
        Runnable r = new MyRunnable();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }
}

  1. 执行步骤:A.第一线程锁的对象,再执行操作 B.当第二线程遇到锁的对象时,发现对象被锁,则等待第一线程释放对象后再执行

3.3 第二种解决方式-同步方法

  1. 同步方法可以是普通成员方法,也可以静态方法

  2. 说明:
    A.成员方法锁的对象就是this
    B.静态方法锁的对象是当前类的class对象(类名.class),因为静态优先对象,所以不能使用this关键字

  3. 以上面两种方法实现

package qf22020308_work.Demo04;

public class MyRunnable implements Runnable {

    private int a = 0;

    private int b = 0;

    @Override
    public void run() {
        while (a < 100) {
            synchronized (Runnable.class) {  //静态,因为静态优先对象,所以不能使用this关键字
                getVotes1();
            }
        }
        while (b < 100) {
            synchronized (this) {         //非静态方法
                getVotes2();
            }
        }
    }

    public static void getVotes1() {
        //执行操作语句
    }

    public void getVotes2() {
        //执行操作语句
    }
}

3.4 第三种解决方式-实现Lock接口

  1. Lock简介
    A.实现提供了比使用synchronized方法语句可获得的更广泛的锁定操作
    B.此实现允许更灵活的操作
  2. 实现类是:ReentrantLock
  3. 自定义类实现Runnable接口
package qf22020308_lock.Demo01;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

    @Override
    public void run() {
        lock.lock();
        try {
            while (true) {
                if (count < 1) {
                    return;
                }
                System.out.println(Thread.currentThread().getName() + " " + count);
                count--;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

  1. 测试类
package qf22020308_lock.Demo01;

public class Demo01 {
    public static void main(String[] args) {
        MyLock ml = new MyLock();
        Thread t1 = new Thread(ml);
        t1.start();
        Thread t2 = new Thread(ml);
        t2.start();
        Thread t3 = new Thread(ml);
        t3.start();
    }
}

3.5 总结

  1. 三种方法都必须实现Runnable接口
  2. 控制循环都在run()方法内,操作结果可在外面

四、死锁

4.1 简介

  1. A占用B的锁的对象,B占用A的锁的对象,互不谦让,两个线程都处于等待状态,出现了死锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IT阿生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值