多线程基础

多线程

1、概念

  • 进程是一个应用程序。
  • 线程是一个进程中的一个执行单元
  • 一个进程可以启动多个线程
  • 进程之间内存独立不共享
  • 在java语言中,线程之间 ,堆内存和方法区是共享的。栈内存是独立的,一个线程一个栈。
    在这里插入图片描述

实现多线程

1、方式一,编写一个类,继承java.lang.Thread,重写run方法
例如:

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("MyThread"+i);
        }
    }
}
 
public class Test01{
   public static void main(String[] args) {
       MyThread myThread = new MyThread();
       myThread.start();
       for (int i = 0; i < 1000; i++) {
           System.out.println("主线程"+i);
       }
   }
}

运行结果:
在这里插入图片描述
2、方式二,实现Runable接口

public class MyThread02 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("MyThread02"+i);
        }
    }
}
@Test
    public void t2() {
        MyThread02 myThread02 = new MyThread02();
        Thread t1 = new Thread(myThread02);
        t1.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("主线程"+i);
        }
    }

结果:
在这里插入图片描述
3、采用 匿名内部类方式

@Test
    public void t3() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("匿名内部类"+i);
                }
            }
        });
        t1.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("主线程"+i);
        }
    }

结果:
在这里插入图片描述

线程生命周期

在这里插入图片描述

常用操作

1、获取当前线程对象的信息

  • 获取线程的名字:getName()
  • 设置线程的名字:setName()
  • 获取当前线程:Thread.currentThrend();

2、sleep()方法

  • 静态方法:Thread.sleep(1000);
  • 参数是毫秒
  • 作用:让当前线程进入休眠,进入“阻塞状态”,放弃占用CPU时间片,让给其他线程使用
    面试题:
public class MyThread03 {
    public static void main(String[] args) {
        Thread t1 = new T1();
        t1.start();
        try {
            //因为sleep是静态方法所以此处其实是Thread.sleep是当前主线程睡眠进入阻塞状态
            t1.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("hello world!");
    }
}

class T1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            Thread thread = Thread.currentThread();
            System.out.println(thread+"-->"+i);
        }
    }
}

结果:hello world! 隔了5秒后输出
在这里插入图片描述

3、interrupt()方法:中断线程的睡眠,唤醒sleep的线程,利用异常的方式。

4、终止线程推荐方法:

public class MyThread04 {
    public static void main(String[] args) {
        T2 t2 = new T2();
        t2.setName("t2");
        t2.start();
        try {
            //主线程sleep5秒
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //5秒之后就会停止T2线程
        t2.run = false;
    }
}

class T2 extends Thread {
    boolean run = true;
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if(run) {
                System.out.println(Thread.currentThread() + "-->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                //这里会终止线程,比如可以在终止的时候保存没保存的文件
                return;
            }
        }
    }
}

在这里插入图片描述

5、线程调度

1、常见的线程调度模型

  • 抢占式调度模型:线程的优先级越高,抢到的CPU时间的概率就搞一些。(Java采用的就是)
  • 均分式调度模型:平均分配CPU时间片,每个线程占用的时间片长度一样。

2、线程调度的方法

  • setPriority(int newPriority) 更改线程优先级
  • getPriority():获取线程优先级
  • 最低1,最高10,默认5
  • 静态方法:yield():暂停当前执行的线程,回到就绪状态,让出时间片,执行其他线程。不是阻塞
  • 实例方法:void join() 合并线程

6、线程安全

1、数据在多线程并发的环境下存在安全问题的条件

  • 多线程并发
  • 存在数据共享
  • 共享数据有修改的行为

2、解决线程安全

  • 线程同步机制synchronized在这里插入图片描述
    3、死锁
public class MyThread05 {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        T3 t3 = new T3(o1,o2);
        T4 t4 = new T4(o1,o2);
        t3.start();
        t4.start();
    }
}

class T3 extends Thread {
    private Object o1;
    private Object o2;

    public T3(Object o1,Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o1) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2) {

            }
        }
    }
}

class T4 extends Thread {
    private Object o1;
    private Object o2;

    public T4(Object o1,Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o2) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1) {

            }
        }
    }
}

开发中避免使用synchronzed嵌套

在开发中如何解决线程安全问题

1、尽量使用局部变量代替“实例变量和静态变量”
2、如果必须是实例变量,那么可以考虑创建多个对象,这样的话实例变量的内存就不是共享的。
3、如果不能使用局部变量,那就只能选择synchronized。

守护线程

1、Java中线程分为两大类

  • 用户线程
  • 守护线程(后台线程),比如垃圾回收线程(守护线程)
    • 特点:一般守护线程就是一个死循环,所有的用户线程只要结束,守护线程自动结束;(主线程main方法是一个用户线程)

2、实现

public final void setDaemon(boolean on)
将此线程标记为daemon线程或用户线程。 当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。
线程启动前必须调用此方法。

参数
on - 如果 true ,将此线程标记为守护线程

定时器

1、作用

  • 间隔特定的时间,执行特定的程序

2、实现

  • 利用sleep方法
  • java.util.Timer
  • 实际开发中,spring框架提供SpringTask框架

3、coding

public class MyTimeTask {
    public static void main(String[] args) throws ParseException {
        //设置起始时间,将给定的时间转成Date对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse("2020-11-26 16:30:50");
        //创建定时器对象
        Timer timer = new Timer();
        /*
        schedule(TimerTask task, Date firstTime, long period)
        从指定的时间开始 ,对指定的任务执行重复的固定延迟执行 。
         */
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                Date now = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String format = sdf.format(now);
                System.out.println(format+"  备份了");
            }
        },date,1000*5);
    }
}

在这里插入图片描述

实现多线程的第三种方式实现Callable接口(java8新特性)

该方式可以获取线程的返回值

public class MyCallableThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*
        创建一个FutureTask,该任务将在运行时执行给定的Callable。 参数: 可调用–可调用任务
         */
        FutureTask futureTask = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println(Thread.currentThread()+"执行");
                Thread.sleep(5000);
                System.out.println(Thread.currentThread()+"结束");
                return "线程返回的结果";
            }
        });
        Thread t = new Thread(futureTask);
        t.start();
        //这一步会导致主线程阻塞,因为主线程要获取线程的返回结果,要等该线程执行完毕才能接着执行自己的内容;
        futureTask.get();
        System.out.println("主线程。。。");
    }
}

在这里插入图片描述

Object中的wait和notify方法

1、不是线程中的方法,是Java中任何一个对象都有的
2、wait()方法作用

  • Object o = new Object(); o. wait();
  • 让正在o对象活动的线程进入等待状态,无限期等直到被唤醒。(会让当前线程进入等待状态)
  • 调用notify()方法,将进入等待状态的线程唤醒。

生产者消费者代码演示:

public class ProductConsumer {
    public static void main(String[] args) {
        List list = new ArrayList();
        Product product = new Product(list);
        Consumer consumer = new Consumer(list);
        Thread t1 = new Thread(product);
        Thread t2 = new Thread(consumer);
        t1.start();
        t2.start();
    }
}

//生产者
class Product implements Runnable {
    //仓库
    private List list;

    public Product(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        //不停的生产对象
        while (true) {
            //将共享对象锁住
            synchronized (list) {
                if (list.size() > 0) {
                    try {
                        //如果已经有产品了,就停止生产,等待消费者消费
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //没有产品就 生产
                Object o = new Object();
                list.add(o);
                System.out.println("生产者生产中。。。"+o);
                list.notifyAll(); //唤醒所有线程,但并不释放锁
            }
        }
    }
}

//消费者
class Consumer implements Runnable {
    //仓库
    private List list;

    public Consumer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        //不停的消费产品
        while (true) {
            synchronized (list) {
                if (list.size() == 0) {
                    //如果没有产品了,就等待生产
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果有产品就消费
                Object remove = list.remove(0);
                System.out.println("消费者消费中。。。"+remove);
                //唤醒所有线程,不释放锁
                list.notifyAll();
            }
        }
    }
}

在这里插入图片描述

练习

在这里插入图片描述

public class Product implements Runnable{
    private MyEx num;

    public Product(MyEx num) {
        this.num = num;
    }

    @Override
    public void run() {
        while (num.num < 100) {
            synchronized (num) {
                if (num.num % 2 == 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"-->"+num.num++);
                num.notifyAll();
            }
        }
    }
}
public class Consumer implements Runnable{
    private MyEx num;

    public Consumer(MyEx num) {
        this.num = num;
    }

    @Override
    public void run() {
        while (num.num < 100) {
            synchronized (num) {
                if (num.num % 2 != 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"-->"+num.num++);
                num.notifyAll();
            }
        }
    }
}
public class MyEx {
    int num = 1;
    public static void main(String[] args) {
        MyEx myEx = new MyEx();
        Product product = new Product(myEx);
        Consumer consumer = new Consumer(myEx);
        Thread t1 = new Thread(product);
        t1.setName("t1");
        Thread t2 = new Thread(consumer);
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

在这里插入图片描述

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页