多线程学习

1.概念

线程:线程是操作系统能够进行运算调度的最小单位,它被包含在进程中,是进程中的实际运作单位。是应用软件中相互独立,可以同时运行的功能。

并发:在同一时刻,有多个指令在单个CPU上交替执行

并行:在同一时刻,有多个指令在多个CPU上同时进行

2.多线程实现方式

1.继承Thread类的方式进行实现

1.自己定义一个类继承Thread

2.重写run方法

3.创建子类的对象,并启动线程

package com.hzj.a01threadcase1;


public class ThreadDemo {
    public static void main(String[] args) {
        /*
        * 多线程的第一种启动方式:
        *   1.自己定义一个类继承Thread
        *   2.重写run方法
        *   3.创建子类的对象,并启动线程
        * */

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("线程一");
        t2.setName("线程二");
        // 开启线程
        t1.start();
        t2.start();
    }
}
package com.hzj.a01threadcase1;

public class MyThread extends Thread{

    @Override
    public void run() {
        // 书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "Hello World");
        }
    }
}

2.实现Runnable接口的方式进行实现

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

2.重写里面的run方法

3.创建自己的类的对象

4.创建一个Thread类的对象,并开启线程

package com.hzj.a02threadcase2;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
        * 多线程第二次启动方式:
        *   1.自己定义一个类实现Runnable接口
        *   2.重写里面的run方法
        *   3.创建自己的类的对象
        *   4.创建一个Thread类的对象,并开启线程
        * */
        // 创建MyRun的对象
        // 表示多线程要执行的任务
        MyRun mr = new MyRun();

        // 创建线程对象
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        t1.setName("线程1");
        t2.setName("线程2");

        // 开启线程
        t1.start();
        t2.start();
    }
}
package com.hzj.a02threadcase2;

public class MyRun implements Runnable{
    @Override
    public void run() {
        // 书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            // 获取到当前线程的对象
            Thread t = Thread.currentThread();
            System.out.println(t.getName() + "Hello World");
        }
    }
}

3.利用Callable接口和Future接口方式实现

package com.hzj.a03Threadcase3;

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

public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*
        * 多线程的第三种实现方式:
        *   特点:可以获取到多线程运行的结果
        *   1.创建一个类MyCallable实现Callable接口
        *   2.重写call(是有返回值的,表示多线程运行的结果)
        *   3.创建MyCallable的对象(表示多线程要执行的任务)
        *   4.创建FutureTask的对象(作用管理多线程运行的结果)
        *   5.创建Thread类的对象,并启动(表示线程)
        * */

        // 创建MyCallable的对象(表示多线程要执行的任务)
        MyCallable mc = new MyCallable();
        // 创建FutureTask的对象(作用管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<>(mc);
        // 创建线程对象
        Thread t1 = new Thread(ft);
        // 启动线程
        t1.start();
        // 获取多线程运行的结果
        Integer result = ft.get();
        System.out.println("result = " + result);
    }
}
package com.hzj.a03Threadcase3;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 求1~100之间的和
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum = sum + i;
        }
        return sum;
    }
}

3.常见的成员方法

方法名称说明
String getName()返回此线程的名称
void setName(String name)设置线程的名字(构造方法也可以设置名字)
static Thread currentThread()获取当前线程的对象
static void sleep(long time)让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority)设置线程的优先级 1-10
final int getPriority()获取线程的优先级
final void setDaemon(boolean on)设置为守护线程
public static void yield()出让线程/礼让线程
public static void join()插入线程/插队线程

基础方法 

package com.hzj.a04threadmethod;

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        /*
        * String getName()
        *   如果没用给线程设置名称,线程有默认的  格式: Thread-X (从0开始)
        * void setName(String name)
        *   构造方法也可以设置名字,需要继承Thread的构造方法
        * static Thread currentThread()
        *   当JVM虚拟机启动之后,会自动的启动多条线程
        *   其中有一条线程就叫做main线程
        *   它的作用就是取调用main方法,并执行里面的代码
        *
        *  static void sleep(long time)
        *   哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
        *   方法的参数就表示睡眠的时间,单位毫秒
        *   当时间到了之后,线程会自动的醒来,继续执行下面的代码
        * */

        MyThread t1 = new MyThread("飞机");
        MyThread t2 = new MyThread("坦克");

        t1.start();
        t2.start();

        // 哪条线程执行到这个方法,此时获取的就是哪条线程的对象
        Thread t = Thread.currentThread();
        String name = t.getName();
        System.out.println(name);

        System.out.println(321);
        Thread.sleep(10000);
        System.out.println(123);
    }
}

优先级设置 

package com.hzj.a05threadmethod2;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
        *  setPriority(int newPriority)  默认是5  数字越大,优先级越高
        *  final int getPriority()
        *
        * */
        // 创建线程要执行的参数对象
        MyRunnable mr = new MyRunnable();
        // 创建线程对象
        Thread t1 = new Thread(mr, "飞机");
        Thread t2 = new Thread(mr, "坦克");

        System.out.println(t1.getPriority());
        System.out.println(t2.getPriority());

        t1.setPriority(1);
        t2.setPriority(10);

        t1.start();
        t2.start();
    }
}

 守护线程

package com.hzj.a06threadmethod3;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
        *  final void setDaemon(boolean on) 设置为守护线程
        *      当其它的非守护线程执行完毕之后,守护线程会陆续结束,即便线程内部的代码没用执行完也会停止,但不是立即停止
        *
        * */

        MyThread1 t1 = new MyThread1();
        MyThread2 t2 = new MyThread2();

        t1.setName("神子");
        t2.setName("只狼");

        // 把第二个线程设置为守护线程
        t2.setDaemon(true);

        t1.start();
        t2.start();
    }
}

礼让线程

package com.hzj.a07threadmethod4;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
        * public static void yield  出让线程/礼让线程  可以让结果尽可能的均匀
        *
        * */

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("飞机");
        t2.setName("坦克");

        t1.start();
        t2.start();
    }
}
package com.hzj.a07threadmethod4;

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

            // 表示出让当前CPU的执行权
            Thread.yield();
        }
    }
}

插入线程

package com.hzj.a08threadmethod5;

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        /*
        * public final void join() 插入线程/插队线程
        *
        * */

        MyThread t = new MyThread();
        t.setName("土豆");
        t.start();
        // 把t线程插入到当前线程之前
        t.join();

        //执行在main线程当中的
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程" + i);
        }
    }
}

4.线程生命周期

 5.同步代码块

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

特点:

        1.锁默认打开,有应该线程进去了,锁自动关闭

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

public class MyThread extends Thread{
    // 表示这个类所有的对象,都共享ticket数据
    static int ticket = 0;

    // 锁对象,必须是唯一的
    static Object object = new Object();
    @Override
    public void run() {
        while (true) {
            // 同步代码块
            synchronized (object){
                if (ticket < 100) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket++;
                    System.out.println(getName() + "正在卖第" + ticket + "张票!");
                } else {
                    break;
                }
            }
        }
    }
}

6.同步方法

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

格式: 修饰符 synchronized 返回值类型 方法名(方法参数) {...}

特点:

        1.同步方法是锁住方法里面所有的代码

        2.锁对象不能自己指定  当方法是非静态是,锁对象是this,即调用者对象。当方法是静态时,锁对象是当前类的字节码文件对象。

public class MyRunnable implements Runnable{
    int ticket = 0;
    @Override
    public void run() {
        // 循环
        // 同步代码块
        while (true) {
            if (method()) {
                break;
            }
        }
    }

    private synchronized boolean method() {
        if (ticket == 100) {
            return true;
        } else {
            ticket++;
            System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!");
        }
        return false;
    }
}

7.Lock锁

Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作

Lock中提供了获得锁和释放锁的方法

void lock():获得锁

void unlock():释放锁

Lock是接口不能直接实例化,采用它的实现类ReentrantLock来实例化

ReentrantLock():创建一个ReentrantLock的实例

public class MyThread extends Thread{
    // 表示这个类所有的对象,都共享ticket数据
    static int ticket = 0;

    static Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            // 加锁
            lock.lock();
            try {
                // 同步代码块
                if (ticket < 100) {
                    Thread.sleep(100);
                    ticket++;
                    System.out.println(getName() + "正在卖第" + ticket + "张票!");
                } else {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 解锁
                lock.unlock();
            }
        }
    }
}

8.等待唤醒机制

1.常用方式

public class Cook extends Thread{
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.count == 0) {
                    break;
                } else {
                    if (Desk.foodFlag == 1) {
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("厨师做了一碗面条");
                        Desk.foodFlag = 1;
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}
public class Foodie extends Thread{
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.count == 0) {
                    break;
                } else {
                    // 判断桌子上是否有食物
                    if (Desk.foodFlag == 0) {
                        // 如果没有,就等待
                        try {
                            Desk.lock.wait();  // 让当前线程和锁进行绑定
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        // 吃的总数-1
                        Desk.count--;
                        // 如果有,就吃
                        System.out.println("正在吃,还能吃" + Desk.count + "碗!!!");
                        // 吃完之后,唤醒厨师
                        Desk.lock.notifyAll();   // 唤醒和这把锁绑定的所有线程
                        // 修改桌子状态
                        Desk.foodFlag = 0;
                    }
                }
            }
        }
    }
}
public class Desk {
    /* 控制生产者和消费者的执行 */

    // 是否有食物 0:没有 1:有
    public static int foodFlag = 0;

    // 总个数
    public static int count = 10;

    // 锁对象
    public static Object lock = new Object();
}
public class ThreadDemo {
    public static void main(String[] args) {
        Cook c = new Cook();
        Foodie f = new Foodie();

        c.setName("厨师");
        f.setName("食客");

        c.start();
        f.start();
    }
}

2.阻塞队列方式

public class Cook extends Thread{

    ArrayBlockingQueue<String> queue;

    public Cook(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                queue.put("食物");
                System.out.println("厨师放了一份食物");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Foodie extends Thread{

    ArrayBlockingQueue<String> queue;

    public Foodie(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                String take = queue.take();
                System.out.println(take);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        // 生产者和消费者必须使用同一个阻塞队列
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
        Cook c = new Cook(queue);
        Foodie f = new Foodie(queue);

        c.setName("厨师");
        f.setName("食客");

        c.start();
        f.start();
    }
}

9.线程池

1.原理

1.创建一个池子,池子中是空的

2.提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子,下回再次提交任务时,不需要在创建新的线程,直接复用已有的线程即可。

3.但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待。

4.当核心线程满时,再提交任务就会排队,当队伍也满时,会创建临时线程,当临时线程也满时,会触发任务拒绝策略。

2.代码实现

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。

newCachedThreadPool():创建一个没有上限的线程池(int类型的最大数量,21亿)

newFixedThreadPool(int nThreads):创建有上限的线程池

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        // 获取线程池对象
//        ExecutorService pool1 = Executors.newCachedThreadPool(); // 无上限

        ExecutorService pool1 = Executors.newFixedThreadPool(3);
        // 提交任务
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        // 销毁线程池
        pool1.shutdown();
    }
}

3.拒绝策略

当核心线程,临时线程,等待队列全部占满之后会触发拒绝策略

任务拒绝策略说明
ThreadPoolExecuter.AbortPolicy默认策略:丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecuter.DiscardPolicy丢弃任务,但是不抛出异常,不推荐
ThreadPoolExecuter.DiscardOldestPolicy抛弃队列中等待最久的任务,然后把当前任务加入队列中
ThreadPoolExecuter.CallerRunsPolicy调用任务的run()方法绕过线程池直接执行

4.自定义线程池

public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3, // 核心线程数,不能小于0
                6, // 最大线程数,大于等于核心线程数
                60,  // 空闲线程最大存活时间 值
                TimeUnit.SECONDS,  // 空闲线程最大存活时间 单位
                new ArrayBlockingQueue<>(3),  // 任务队列,不能为null
                Executors.defaultThreadFactory(), // 创建线程工厂 不能为null
                new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );
        
        pool.submit(new MyCallable());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值