多线程的相关概念

简介: 多线程的相关概念

多线程的相关概念

1.线程的两种实现方式

继承Thread类,实现Runnable接口

package com.lili.thread;
import sun.awt.windows.ThemeReader;
/**
 * 线程的休眠
 * 在当前线程的执行中,暂停指定的毫秒数 释放cpu的时间片
 *
 * @author: QiJingJing
 * @create: 2021/7/14
 */
public class Test1 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        // 推荐
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        // 启动线程
        myThread.start();
        thread.start();
    }
}
/**
 * 实现线程的第一种方式:继承Thread类
 */
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "-" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
/**
 * 实现线程的第二种方式:实现Runnable接口
 */
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "-" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.Join与中断线程

加入线程,让调用线程先执行完毕,

package com.lili.thread;
/**
 * join方法
 * 加入线程,让调用线程先执行完毕
 *
 * @author: QiJingJing
 * @create: 2021/7/14
 */
public class Test2 {
    public static void main(String[] args) {
        MyRunnable2 myRunnable2 = new MyRunnable2();
        Thread thread2 = new Thread(myRunnable2);
        thread2.start();
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i == 20) {
                try {
                    // 让thread2线程执行完毕
                    thread2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                thread2.interrupt();
            }
        }
    }
}
class MyRunnable2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

中断线程适合自定义标记中断线程:

package com.lili.thread;
/**
 * @author: QiJingJing
 * @create: 2021/7/14
 */
public class Test2 {
    public static void main(String[] args) {
        MyRunnable2 myRunnable2 = new MyRunnable2();
        Thread thread2 = new Thread(myRunnable2);
        thread2.start();
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i == 20) {
                myRunnable2.flag = false;
            }
        }
    }
}
class MyRunnable2 implements Runnable {
    public boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println(Thread.currentThread().getName() + "--" + i++);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3.守护线程与yield

public final void setDaemon(boolean on):将此线程标记为daemon线程或用户线程,当运行的唯一线程是守护线程时,java虚拟机将退出

yield():让出本次cpu执行权,但不一定礼让成功

package com.lili.thread;
/**
 * 守护线程和yield
 *
 * @author: QiJingJing
 * @create: 2021/7/14
 */
public class Test3 {
    public static void main(String[] args) {
        Runnable3 runnable3 = new Runnable3();
        Thread thread = new Thread(runnable3);
        // 线程可以分为守护线程和用户线程,当进程中没有用户线程时,JVM会退出
        // 设置为守护线程
        thread.setDaemon(true);
        //设置优先级可以提高该线程抢得cpu的概率
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
        for (int i = 0; i < 50; i++) {
            System.out.println("main--" + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i == 5) {
                Thread.yield();//让出本次cpu的执行权
            }
        }
    }
}
class Runnable3 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("--" + i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4.线程同步

(1)同步代码块 (2)使用同步方法 (3)使用Lock锁(更灵活的代码控制)

package com.lili.thread;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 1.多线程共享数据时,会发生线程不安全的情况
 * 2、多线程共享数据必须实现同步,
 * 3.实现同步的三种方法:
 * (1 同步代码块
 * (2 使用同步方法
 * (3 使用lock(更灵活的代码控制)
 * 多线程共享数据,会有安全问题,使用同步可以解决安全问题,但同时会牺牲性能,所以同步的代码块尽量保持简短,把不随线程变化的代码移除同步,不要阻塞
 *
 * @author: QiJingJing
 * @create: 2021/7/15
 */
public class Test4 {
    public static void main(String[] args) {
        MyRunnable5 myRunnable5 = new MyRunnable5();
        Thread t1 = new Thread(myRunnable5);
        Thread t2 = new Thread(myRunnable5);
        t1.start();
        t2.start();
    }
}
class MyRunnable5 implements Runnable {
    private int ticket = 10;// 售票
    //    @Override
//    // 可以写方法中,表示为同步方法
//    public /*synchronized*/ void run() {
//        for (int i = 0; i < 300; i++) {
//                synchronized (this) {
//                    if(ticket>0){
//                    ticket--;
//                    try {
//                        Thread.sleep(1000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    System.out.println("你购买的票剩余"+ticket+"张");
//                }
//            }
//        }
//    }
    ReentrantLock lock = new ReentrantLock();
    //lock实现同步
    @Override
    // 可以写方法中,表示为同步方法
    public void run() {
        for (int i = 0; i < 300; i++) {
            lock.lock();//上锁
            try {
                if (ticket > 0) {
                    ticket--;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("你购买的票剩余" + ticket + "张");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//解锁
            }
        }
    }
}

5.死锁

过多的同步有可能出现死锁,死锁的操作一般是在程序运行的时候才可能出现,多线程中要进行资源的共享,就需要同步,但同步过多,就可能造成死锁:

package com.lili.thread;
/**
 * 死锁例子
 *
 * @author: QiJingJing
 * @create: 2021/7/15
 */
public class Test5 {
    private static Object obj1 = new Object();
    private static Object obj2 = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (obj1) {
                System.out.println("obj1");
                synchronized (obj2) {
                    System.out.println("obj2");
                }
            }
        }).start();
        new Thread(() -> {
            synchronized (obj2) {
                System.out.println("obj2");
                synchronized (obj1) {
                    System.out.println("obj1");
                }
            }
        }).start();
    }
}

6,生产者与消费者

(1)管程法

package com.lili.thread;
/**
 * 测试生产者消费者模型(管程法)
 *
 * @author: QiJingJing
 * @create: 2021/7/15
 */
public class Test6 {
    public static void main(String[] args) {
        Buffer buf = new Buffer();
        new Thread(new Productor(buf)).start();
        new Thread(new Consumer(buf)).start();
    }
}
// 生产者
class Productor implements Runnable {
    private Buffer buffer;
    public Productor(Buffer buffer) {
        this.buffer = buffer;
    }
    // 生产
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            buffer.push(new Chicken(i));
            System.out.println("成产了" + i + "只鸡");
        }
    }
}
// 消费者
class Consumer implements Runnable {
    private Buffer buffer;
    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }
    // 生产
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("消费了第" + buffer.pop().getId() + "只鸡");
        }
    }
}
// 产品
class Chicken {
    private int id;
    public Chicken(int id) {
        this.id = id;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}
// 缓冲区
class Buffer {
    // 需要一个容器大小
    private Chicken[] chickens = new Chicken[10];
    // 容器计数器
    private int count = 0;
    // 生产者放入产品
    public synchronized void push(Chicken chicken) {
        //如果容器满了,需要等待消费者消费
        if (count == chickens.length) {
            //通知消费者消费,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果没满,我们要丢入产品
        chickens[count] = chicken;
        count++;
        // 可以通知消费者消费了
        this.notifyAll();
    }
    //消费者消费产品
    public synchronized Chicken pop() {
        // 判断能否消费
        if (count == 0) {
            //等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果可以消费
        count--;
        Chicken chicken = chickens[count];
        // 通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

(2)信号灯法

package com.lili.thread;
/**
 * 生产者与消费者(信号灯法)
 *
 * @author: QiJingJing
 * @create: 2021/7/15
 */
public class Test7 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Thread(new Player(tv)).start();
        new Thread(new Watcher(tv)).start();
    }
}
// 生产者->演员
class Player implements Runnable {
    private TV tv;
    public Player(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                this.tv.play("快乐大本营播放中");
            } else {
                this.tv.play("抖音:记录美好生活");
            }
        }
    }
}
// 消费者->观众
class Watcher implements Runnable {
    private TV tv;
    public Watcher(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.tv.watch();
        }
    }
}
// 产品-节目
class TV {
    private String voice;// 表演的节目
    private boolean flag = true;
    // 表演
    public synchronized void play(String voice) {
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了" + voice);
        // 通知群众观看
        this.notifyAll();
        this.voice = voice;
        this.flag = !this.flag;
    }
    // 观看
    public synchronized void watch() {
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了" + voice);
        // 通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

7.线程池

背景:经常创建和销毁,使用量特别大的资源,比如并发下的线程,对性能影响很大

思路:提前创建好多个线程,放入线程池,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用,类似生活中的交通工具

package com.lili.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @author: QiJingJing
 * @create: 2021/7/15
 */
public class Test8 {
    public static void main(String[] args) {
        // 1.创建服务,创建线程池
        // 参数为线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(new MyRunnable7());
        service.execute(new MyRunnable7());
        service.execute(new MyRunnable7());
        service.execute(new MyRunnable7());
        //2. 关闭链接
        service.shutdown();
    }
}
class MyRunnable7 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}


目录
相关文章
|
4月前
|
Go 调度 开发者
[go 面试] 深入理解进程、线程和协程的概念及区别
[go 面试] 深入理解进程、线程和协程的概念及区别
|
22天前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
39 4
|
6月前
|
分布式计算 JavaScript 前端开发
多线程、多进程、协程的概念、区别与联系
多线程、多进程、协程的概念、区别与联系
101 1
|
6月前
|
分布式计算 并行计算 安全
在Python Web开发中,Python的全局解释器锁(Global Interpreter Lock,简称GIL)是一个核心概念,它直接影响了Python程序在多线程环境下的执行效率和性能表现
【6月更文挑战第30天】Python的GIL是CPython中的全局锁,限制了多线程并行执行,尤其是在多核CPU上。GIL确保同一时间仅有一个线程执行Python字节码,导致CPU密集型任务时多线程无法充分利用多核,反而可能因上下文切换降低性能。然而,I/O密集型任务仍能受益于线程交替执行。为利用多核,开发者常选择多进程、异步IO或使用不受GIL限制的Python实现。在Web开发中,理解GIL对于优化并发性能至关重要。
70 0
|
7月前
|
Java
Java中的多线程编程:概念、实现与挑战
【5月更文挑战第30天】本文深入探讨了Java中的多线程编程,涵盖了多线程的基本概念、实现方法以及面临的挑战。通过对Java多线程编程的全面解析,帮助读者更好地理解多线程在Java中的应用,提高程序的性能和效率。
|
3月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
75 0
|
4月前
|
缓存 前端开发 JavaScript
一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
【8月更文挑战第11天】一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
41 0
|
5月前
|
Java 程序员 调度
Java中的多线程编程:概念、实现及性能优化
【5月更文挑战第85天】本文主要探讨了Java中的多线程编程,包括其基本概念、实现方式以及如何进行性能优化。首先,我们将介绍多线程的基本概念,然后详细讨论如何在Java中实现多线程,包括继承Thread类和实现Runnable接口两种方式。最后,我们将探讨一些提高多线程程序性能的策略,如使用线程池和减少同步开销等。
|
5月前
|
监控 Java 调度
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
82 1
|
5月前
|
缓存 Linux 编译器
【Linux】多线程——线程概念|进程VS线程|线程控制(下)
【Linux】多线程——线程概念|进程VS线程|线程控制(下)
77 0