多线程&JUC

本文介绍了Java中的多线程概念,包括并发和并行的区别,以及三种实现多线程的方法:继承Thread类、实现Runnable接口和使用Callable/Future接口。同时,详细讨论了线程的同步机制,如同步代码块、同步方法、Lock锁以及死锁和等待唤醒机制。最后提到了线程池的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.什么是多线程

2.并发和并行

3.多线程的实现方式 

3.1 继承Thread类的方式实现

3.2 实现Runnable接口的方式进行实现

3.3 利用Callable接口和Future接口方式实现(有返回值,表示线程运行结果)

 4.常见的成员方法

 5. 线程的生命周期

 6.同步代码块(把操作共享数据的代码锁起来)

 7.同步方法(把synchronized关键字加到方法上)

8.Lock锁 (前面的同步代码快中是会自动上锁与解锁,这里可以手动控制)

 9. 死锁(是一个错误,如果定义了两个锁,然后执行的时候两个锁互相嵌套,则此时是运行不下去的)

10.等待唤醒机制(即打破线程的随机性,使他们轮流执行)

10.等待唤醒机制(阻塞队列方式实现)

11.线程的状态(因为当线程抢到了CPU执行权的时候,虚拟机把当前的线程交给操作系统,所以他没有运行状态)

 12.线程池


1.什么是多线程

        应用软件中互相独立,可以同时运行的功能,即可以让程序同时做多个事情

多线程的作用:提高效率

2.并发和并行

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

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

        假设我们电脑上的CPU是2核4线程的,那么我们当我们正在进行的线程<=4时,是并行的状态,当线程数>4时,是并发与并行同时发生的状态。

3.多线程的实现方式 

3.1 继承Thread类的方式实现

1.Test方法中

package study03Thread;

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

        myThread01.setName("线程1"); //设置myThread01线程的名字
        myThread02.setName("线程2");

        myThread01.start();  // 开启线程
        myThread02.start();  // 开启线程
    }
}

2. 子类对象中

package study03Thread;

public class MyThread extends Thread{

    @Override
    public void run(){
        for (int i = 0; i < 20; i++) {
            System.out.println(getName()+"Hello Thread");//getName()可以获取该线程的名字
        }

    }
}

3. 运行结果

C:\Users\pearl\.jdks\corretto-11.0.15\bin\java.exe "-javaagent:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\lib\idea_rt.jar=51186:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\后端开发教程\java_study_record\out\production\01-java基础 study03Thread.Thread01
线程2Hello Thread
线程2Hello Thread
线程2Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程1Hello Thread
线程2Hello Thread
线程2Hello Thread
线程2Hello Thread
线程2Hello Thread
线程2Hello Thread
线程2Hello Thread
线程2Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread
线程1Hello Thread

Process finished with exit code 0

可以看到两个线程是交替进行的,且多次运行的结果不一样

3.2 实现Runnable接口的方式进行实现

1.Test方法

package study03Thread;

public class Thread02 {
    public static void main(String[] args) {
        /*
        * 多线程的第二种启动方式
        * 1.自己定义一个类实现Runnable接口
        * 2.重写里面的run方法、
        * 3.创建自己类的对象(表示多线程的任务)
        * 4.创建一个Thread对象,并开启线程
        * */
        MyThreadRunnable mtr = new MyThreadRunnable();
        Thread th01 = new Thread(mtr);
        Thread th02 = new Thread(mtr);
        Thread th03 = new Thread(mtr);

        th01.setName("线程1");
        th02.setName("线程2");
        th03.setName("线程3");

        th01.start();
        th02.start();
        th03.start();

    }
}

2.子类

package study03Thread;

public class MyThreadRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            //        获取当前的Thread线程对象,
            System.out.println(Thread.currentThread().getName()+"Hello Runnable");
//            由于当前类是实现的Runnable接口,而该接口没getName方法
        }

    }
}

3.运行结果

C:\Users\pearl\.jdks\corretto-11.0.15\bin\java.exe "-javaagent:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\lib\idea_rt.jar=51977:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\后端开发教程\java_study_record\out\production\01-java基础 study03Thread.Thread02
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程3Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程1Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable
线程2Hello Runnable

Process finished with exit code 0

3.3 利用Callable接口和Future接口方式实现(有返回值,表示线程运行结果)

1.Test方法

package study03Thread;

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

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

        MyCallable mc = new MyCallable();
//        创建FutureTask对象 下行代码表示,用ft管理mc运行的结果
        FutureTask<Integer> ft = new FutureTask<Integer>(mc);
        Thread th = new Thread(ft);
        th.start();
        Integer result = ft.get();//表示获取线程运行的结果
        System.out.println(result);


    }
}

2.子类

package study03Thread;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        //返回一个泛型类型的数据
        return 100;
    }
}

 3.运行结果

C:\Users\pearl\.jdks\corretto-11.0.15\bin\java.exe "-javaagent:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\lib\idea_rt.jar=52358:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\后端开发教程\java_study_record\out\production\01-java基础 study03Thread.Thread03
100

Process finished with exit code 0

 4.常见的成员方法

 5. 线程的生命周期

sleep方法会让线程休眠,休眠事件到了之后,在就绪状态,不能立刻执行代码

 6.同步代码块(把操作共享数据的代码锁起来)

 例题:电影院中三个窗口卖票,有一个电影共有10张票

同步代码块之前:

//主方法中
package study03Thread;

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

//        创建线程对象
        MyThreadTicket myticket01 = new MyThreadTicket();
        MyThreadTicket myticket02 = new MyThreadTicket();
        MyThreadTicket myticket03 = new MyThreadTicket();
//        起名字
        myticket01.setName("窗口1");
        myticket02.setName("窗口2");
        myticket03.setName("窗口3");
//        开启线程
        myticket01.start();
        myticket02.start();
        myticket03.start();
    }
}
//子类对象
package study03Thread;

public class MyThreadTicket extends Thread{
    int ticket =0;
    @Override
    public void run(){
        while (true){
            if (ticket<10){
                ticket++;
                System.out.println(getName()+"正在卖第"+ticket+"票");
            }
            else break;
        }
    }
}
C:\Users\pearl\.jdks\corretto-11.0.15\bin\java.exe "-javaagent:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\lib\idea_rt.jar=64770:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\后端开发教程\java_study_record\out\production\01-java基础 study03Thread.Ticket
窗口2正在卖第1票
窗口2正在卖第2票
窗口2正在卖第3票
窗口2正在卖第4票
窗口2正在卖第5票
窗口2正在卖第6票
窗口2正在卖第7票
窗口2正在卖第8票
窗口2正在卖第9票
窗口2正在卖第10票
窗口3正在卖第1票
窗口3正在卖第2票
窗口3正在卖第3票
窗口3正在卖第4票
窗口3正在卖第5票
窗口3正在卖第6票
窗口3正在卖第7票
窗口3正在卖第8票
窗口3正在卖第9票
窗口3正在卖第10票
窗口1正在卖第1票
窗口1正在卖第2票
窗口1正在卖第3票
窗口1正在卖第4票
窗口1正在卖第5票
窗口1正在卖第6票
窗口1正在卖第7票
窗口1正在卖第8票
窗口1正在卖第9票
窗口1正在卖第10票

Process finished with exit code 0

从上面的运行结果我们可以发现,这三个窗口是互相独立的,各自卖了10张票,与我们的要求不符合

同步代码块之后:

test主方法不变

修改后的子类对象:

package study03Thread;

public class MyThreadTicket extends Thread{
    static int ticket =0;
    static Object obj = new Object();
//这里的锁一定要唯一,如果不唯一的话,可能发生多个线程同时执行同步代码块中的代码
    @Override
    public void run(){
        while (true){
            synchronized (obj){ //这行代码一定要写在循环里面,这行代码意味着三个线程在抢{}内的资源,如果写在循环外面,就会发生第一个抢到的线程卖了所有的票
                if (ticket<10){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket++;
                    System.out.println(getName()+"正在卖第"+ticket+"票");
                }
                else break;
            }

        }
    }
}

 运行结果如下,满足要求,意满离:

C:\Users\pearl\.jdks\corretto-11.0.15\bin\java.exe "-javaagent:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\lib\idea_rt.jar=65110:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\后端开发教程\java_study_record\out\production\01-java基础 study03Thread.Ticket
窗口1正在卖第1票
窗口1正在卖第2票
窗口3正在卖第3票
窗口2正在卖第4票
窗口3正在卖第5票
窗口1正在卖第6票
窗口3正在卖第7票
窗口2正在卖第8票
窗口3正在卖第9票
窗口3正在卖第10票

Process finished with exit code 0

 7.同步方法(把synchronized关键字加到方法上)

8.Lock锁 (前面的同步代码快中是会自动上锁与解锁,这里可以手动控制)

使用Lock锁的子类

package study03Thread;

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

public class MyThreadTicket extends Thread{
    static int ticket =0;
//    创建锁对象(加static关键字表示唯一)
    static Lock lock = new ReentrantLock();
    @Override
    public void run(){
        while (true){
            lock.lock();
            try {
                if (ticket<10){
                    Thread.sleep(100);
                    ticket++;
                    System.out.println(getName()+"正在卖第"+ticket+"票");
                }
                else break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
//               为了防止重复解锁或未解锁的问题,将解锁代码放到finally中
                lock.unlock();
            }
        }
    }
}

 9. 死锁(是一个错误,如果定义了两个锁,然后执行的时候两个锁互相嵌套,则此时是运行不下去的)

10.等待唤醒机制(即打破线程的随机性,使他们轮流执行)

消费者:吃货

package study04Threaddengtaihuanxing;

public class Foodie extends Thread{
//    消费者
    /*
    * 1.循环
    * 2.同步代码快
    * 3.判断临界值
    * 4.没到
    * 5.到了
    * */
    @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 {//是就执行操作,然后唤醒其他线程等待
                        Desk.count--;
                        System.out.println("吃货正在吃面条,还能吃"+Desk.count+"碗");
                        Desk.foodflag = 0; //修改桌子状态,表示已经吃完了
                        Desk.lock.notifyAll();//即吃货吃完了,该叫厨师做了
                    }
                }
            }
        }

    }
}

生产者:厨师

package study04Threaddengtaihuanxing;

public class Cooker extends Thread{
//    生产者
/*
 * 1.循环
 * 2.同步代码快
 * 3.判断临界值
 * 4.没到
 * 5.到了
 * */
    @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();
                    }
                }
            }
        }

    }
}

第三者:桌子

package study04Threaddengtaihuanxing;

public class Desk{
//    第三者控制线程

//    第几条线程执行的判断条件:桌子是否有面条,0:没有  1:有
    public static int foodflag=0;

//    临界值,,最多是几碗
    public static int count=10;

//    创建锁对象
    public static Object lock = new Object();

}

主方法:

package study04Threaddengtaihuanxing;

public class Test {
    public static void main(String[] args) {
//        创建对象
        Cooker c = new Cooker();
        Foodie f = new Foodie();
//        设置名字
        c.setName("厨师");
        f.setName("吃货");
//        启动线程
        c.start();
        f.start();
    }
}

运行结果:

C:\Users\pearl\.jdks\corretto-11.0.15\bin\java.exe "-javaagent:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\lib\idea_rt.jar=50578:D:\Software\Java\IntelliJ IDEA Community Edition 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\后端开发教程\java_study_record\out\production\01-java基础 study04Threaddengtaihuanxing.Test
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃9碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃8碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃7碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃6碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃5碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃4碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃3碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃2碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃1碗
厨师已经做好饭了,饭在桌子上
吃货正在吃面条,还能吃0碗

Process finished with exit code 0

10.等待唤醒机制(阻塞队列方式实现)

 消费者:吃货

package study05Thread;

import java.util.concurrent.ArrayBlockingQueue;

public class Foodie extends Thread{
    ArrayBlockingQueue<String> qu;

    public Foodie(ArrayBlockingQueue<String> qu) {
        this.qu = qu;
    }
    @Override
    public void run(){
        while (true){
//            不断从阻塞队列中获取食物
            try {
                String food = qu.take();
                System.out.println(food);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

生产者:厨师

package study05Thread;

import java.util.concurrent.ArrayBlockingQueue;

public class Cook extends Thread{
    ArrayBlockingQueue<String> qu;
    public Cook(ArrayBlockingQueue<String> qu) {
        this.qu = qu;
    }

    @Override
    public void run(){
        while (true){
//            不断的把饭放到队列中
            try {
                qu.put("面条");
                System.out.println("厨师做了一碗面条");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

主方法:

package study05Thread;

import java.util.concurrent.ArrayBlockingQueue;

public class Test {
    public static void main(String[] args) {
        /*
        * 需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
        * 细节:
        *       生产者和消费者必须使用同一个阻塞队列
        * */
//        创建阻塞队列对象
        ArrayBlockingQueue<String> qu = new ArrayBlockingQueue<>(1);

        Foodie f = new Foodie(qu);
        Cook c =new Cook(qu);

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

    }
}

11.线程的状态(因为当线程抢到了CPU执行权的时候,虚拟机把当前的线程交给操作系统,所以他没有运行状态)

 12.线程池

 线程池代码实现:

主方法:

package study09ThreadPool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
    public static void main(String[] args) {
        /*
        * 1.获取线程池对象
        * 2.提交任务
        * 3.销毁线程池(可以不写这一步)
        * */
//        1.获取线程池对象
        ExecutorService pool = Executors.newCachedThreadPool();
//        2.提交任务  有几个提交代码表示新建了几个线程
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
//        3.销毁线程池
        pool.shutdown();
    }
}

子类中:

package study09ThreadPool;

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

最大并行数(线程池最好)

//向java虚拟机返回可用处理器的数目
int count = Runtime.getRuntime().availableProcessors();
System.out.println(count);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值