Java并发代码入门

news/2024/10/21 13:37:16/

Java并发代码入门

    • 1. 第一个线程
    • 2. Java创建线程的5种方式
    • 3. 多线程优势代码
    • 4. 线程的属性
    • 5. 中断线程
      • 1. 使用自定义的变量来作为标志位
      • 2. Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替自定义标志位
    • 6. join
      • 2.5 等待一个线程-join()
        • A中调用B.join表示 B先完成后A再继续
    • 7. 观察线程的所有状态(遍历 Thread.State.values())
    • 8. 观察线程不安全
    • 9. synchronized
    • 9. volatile
    • 10. wait和notify
    • 11. 单例模式
    • 12. 阻塞队列
    • 13. time
    • 14. 线程池

1. 第一个线程

java">import java.util.Random;
public class ThreadDemo {//注意 线程是private的private static class MyThread extends Thread {@Overridepublic void run() {Random random = new Random();while (true) {// 打印线程名称System.out.println(Thread.currentThread().getName());try {// 随机停止运行 0-9 秒Thread.sleep(random.nextInt(10));} catch (InterruptedException e) {e.printStackTrace();}}}}public static void main(String[] args) {MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();t1.start();t2.start();t3.start();Random random = new Random();while (true) {// 打印线程名称System.out.println(Thread.currentThread().getName());try {Thread.sleep(random.nextInt(10));} catch (InterruptedException e) {// 随机停止运行 0-9 秒e.printStackTrace();}}}
}

2. Java创建线程的5种方式

java">class MyThread1 extends Thread{@Overridepublic void run() {System.out.println("继承Thread类 创建线程");}
}class MyThread2 implements Runnable{@Overridepublic void run() {System.out.println("实现Runnable接口 创建线程");}
}class m{public static void main(String[] args) {MyThread1 t1 = new MyThread1();Thread t2 = new Thread(new MyThread2());Thread t3 = new Thread(){@Overridepublic void run(){System.out.println("内部类创建Thread 子对象");}};Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("内部类实现Runnable接口 创建线程");}});Thread t5 = new Thread(()-> System.out.println("lamba创建runnable"));t1.start();t2.start();t3.start();t4.start();t5.start();}
}

3. 多线程优势代码

java">public class ThreadAdvantage {// 多线程并不一定就能提高速度,可以观察,count 不同,实际的运行效果也是不同的private static final long count = 10_0000_0000;public static void main(String[] args) throws InterruptedException {// 使用并发方式concurrency();// 使用串行方式serial();}private static void concurrency() throws InterruptedException {long begin = System.nanoTime();// 利用一个线程计算 a 的值Thread thread = new Thread(new Runnable() {@Overridepublic void run() {int a = 0;for (long i = 0; i < count; i++) {a--;}}});thread.start();// 主线程内计算 b 的值int b = 0;for (long i = 0; i < count; i++) {b--;}// 等待 thread 线程运行结束thread.join();// 统计耗时long end = System.nanoTime();double ms = (end - begin) * 1.0 / 1000 / 1000;System.out.printf("并发: %f 毫秒%n", ms);}private static void serial() {// 全部在主线程内计算 a、b 的值long begin = System.nanoTime();int a = 0;for (long i = 0; i < count; i++) {a--;}int b = 0;for (long i = 0; i < count; i++) {b--;}long end = System.nanoTime();double ms = (end - begin) * 1.0 / 1000 / 1000;System.out.printf("串行: %f 毫秒%n", ms);}
}

4. 线程的属性

java">public class ThreadDemo {public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {System.out.println(Thread.currentThread().getName() + ": 我还
活着");Thread.sleep(1 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ": 我即将死去");});System.out.println(Thread.currentThread().getName() + ": ID: " + thread.getId());System.out.println(Thread.currentThread().getName() + ": 名称: " + thread.getName());System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState());System.out.println(Thread.currentThread().getName() + ": 优先级: " + thread.getPriority());System.out.println(Thread.currentThread().getName() + ": 后台线程: " + thread.isDaemon());System.out.println(Thread.currentThread().getName() + ": 活着: " + thread.isAlive());System.out.println(Thread.currentThread().getName() + ": 被中断: " + thread.isInterrupted());thread.start();while (thread.isAlive()) {}System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState());}
}

5. 中断线程

1. 使用自定义的变量来作为标志位

示例-1: 使用自定义的变量来作为标志位.

  • 需要给标志位上加 volatile 关键字(这个关键字的功能后面介绍).public volatile boolean isQuit = false;
java">public class ThreadDemo private static class MyRunnable implements Runnable {	public volatile boolean isQuit = false;@Overridepublic void run() {while (!isQuit) {System.out.println(Thread.currentThread().getName()+ ": 别管我,我忙着转账呢!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+ ": 啊!险些误了大事");}}public static void main(String[] args) throws InterruptedException {MyRunnable target = new MyRunnable();Thread thread = new Thread(target, "李四");System.out.println(Thread.currentThread().getName()+ ": 让李四开始转账。");thread.start();Thread.sleep(10 * 1000);System.out.println(Thread.currentThread().getName()+ ": 老板来电话了,得赶紧通知李四对方是个骗子!");target.isQuit = true;}
}

2. Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替自定义标志位

示例-2: 使用 Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替自定义标志位.

Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记.

方法说明
public void interrupt()中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位
public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后清除标志位。
public boolean isInterrupted()判断对象关联的线程的标志位是否设置,调用后不清除标志位

什么叫做清除标志位呢?
就是比如看教室里的灯是否关闭了,如果打开了,清除标志位就是,把灯关了,下次再看就是灯是关的,但第一次看的时候,灯是开得。不清除标志位就表示,不关灯,每次看都是灯是开的

使用 thread 对象的 interrupted() 方法通知线程结束.

java">public class ThreadDemo {private static class MyRunnable implements Runnable {@Overridepublic void run() {// 两种方法均可以while (!Thread.interrupted()) {//while (!Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName()+ ": 别管我,我忙着转账呢!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();System.out.println(Thread.currentThread().getName()+ ": 有内鬼,终止交易!");// 注意此处的 breakbreak;}}System.out.println(Thread.currentThread().getName()+ ": 啊!险些误了大事");}}public static void main(String[] args) throws InterruptedException {MyRunnable target = new MyRunnable();Thread thread = new Thread(target, "李四");System.out.println(Thread.currentThread().getName()+ ": 让李四开始转账。");thread.start();Thread.sleep(10 * 1000);System.out.println(Thread.currentThread().getName()+ ": 老板来电话了,得赶紧通知李四对方是个骗子!");thread.interrupt();}
}

6. join

2.5 等待一个线程-join()

A中调用B.join表示 B先完成后A再继续

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束

java">public class ThreadJoin {//样例一
//    public static void main(String[] args) throws InterruptedException {
//        Runnable target = () -> {
//            for (int i = 0; i < 10; i++) {
//                try {
//                    System.out.println(Thread.currentThread().getName()
//                                       + ": 我还在工作!");
//                    Thread.sleep(1000);
//               } catch (InterruptedException e) {
//                    e.printStackTrace();
//               }
//           }
//            System.out.println(Thread.currentThread().getName() + ": 我结束了!");
//       };
//        Thread thread1 = new Thread(target, "李四");
//        Thread thread2 = new Thread(target, "王五");
//        System.out.println("先让李四开始工作");
//        thread1.start();
        thread1.join();
//        System.out.println("李四工作结束了,让王五开始工作");
//        thread2.start();
        thread2.join();
//        System.out.println("王五工作结束了");
//   }//样例二public static void main(String[] args) {Thread t2 = new Thread(()->{for(int i = 1; i <= 10000;i++){System.out.printf("t2工作%d %n",i);}});Thread t1 = new Thread(()->{//t2先工作try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}for(int i = 1; i <= 10000; i++){System.out.printf("t1工作%d %n",i);}});t2.start();t1.start();}
}

7. 观察线程的所有状态(遍历 Thread.State.values())

线程的状态是一个枚举类型 Thread.State

java">public class ThreadState {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}}
}

8. 观察线程不安全

java">static class Counter {public int count = 0;void increase() {count++;}
}
public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.count);
}

9. synchronized

//调用increase 就会导致锁两次this 
//但是synchronized是可重入的
public class ThreadSecurity_see {static class Counter {public int count = 0;void increase() {synchronized (this){count++;}}void increase2() {synchronized (this){increase();}}}public static void main(String[] args) throws InterruptedException {Counter counter = new Counter();Thread t1 = new Thread(()->{for(int i = 0; i < 50000; i++){counter.increase2();}System.out.println("t1结束");});Thread t2 = new Thread(()->{for(int i = 0; i < 50000; i++){counter.increase2();}System.out.println("t2结束");});System.out.println("开始");System.out.println(counter.count);t1.start();t2.start();t1.join();t2.join();System.out.println("全部结束");System.out.println(counter.count);}
}

9. volatile

java">import java.util.Scanner;public class ThreadVolatile {static class Counter {public int flag = 0;}public static void main(String[] args) {Counter counter = new Counter();Thread t1 = new Thread(() -> {while (counter.flag == 0) {// do nothing}System.out.println("循环结束!");});Thread t2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("输入一个整数:");counter.flag = scanner.nextInt();});t1.start();t2.start();}
}// 执行效果
// 当用户输入 非0值 时, t1 线程循环不会结束. (这显然是一个 bug)
  • t1 读的是自己工作内存中的内容.
  • 当 t2 对 flag 变量进行修改, 此时 t1 感知不到 flag 的变化

如果给 flag 加上 volatile

java">static class Counter {public volatile int flag = 0; 
}
// 执行效果
// 当用户输入非0值时, t1 线程循环能够立即结束.

volatile 不保证原子性

volatile 和 synchronized 有着本质的区别. synchronized 能够保证原子性, volatile 保证的是内存可见性.

代码示例
这个是最初的演示线程安全的代码

  • 给 increase 方法去掉 synchronized
  • 给 count 加上 volatile 关键字.
java">static class Counter {volatile public int count = 0;void increase() {count++;}
}
public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.count);
}

此时可以看到, 最终 count 的值仍然无法保证是 100000.

synchronized 也能保证内存可见性

synchronized 既能保证原子性, 也能保证内存可见性
对上面的代码进行调整:

  • 去掉 flag 的 volatile
  • 给 t1 的循环内部加上 synchronized, 并借助 counter 对象加锁.
java">static class Counter {public int flag = 0; 
}
public static void main(String[] args) {Counter counter = new Counter();Thread t1 = new Thread(() -> {while (true) {synchronized (counter) {if (counter.flag != 0) {break;}}// do nothing}System.out.println("循环结束!");});Thread t2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("输入一个整数:");counter.flag = scanner.nextInt();});t1.start();t2.start();
}

10. wait和notify

java">static class WaitTask implements Runnable {private Object locker;public WaitTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker) {while (true) {try {System.out.println("wait 开始");locker.wait();System.out.println("wait 结束");} catch (InterruptedException e) {e.printStackTrace();}}}}
}
static class NotifyTask implements Runnable {private Object locker;public NotifyTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker) {System.out.println("notify 开始");locker.notify();System.out.println("notify 结束");}}
}
public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t1 = new Thread(new WaitTask(locker));Thread t2 = new Thread(new NotifyTask(locker));t1.start();Thread.sleep(1000);t2.start();
}

11. 单例模式

java">class Singleton {private static volatile Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

12. 阻塞队列

java">BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// 入队列
queue.put("abc");
// 出队列. 如果没有 put 直接 take, 就会阻塞. 
String elem = queue.take();
java">public static void main(String[] args) throws InterruptedException {BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();Thread customer = new Thread(() -> {while (true) {try {int value = blockingQueue.take();System.out.println("消费元素: " + value);} catch (InterruptedException e) {e.printStackTrace();}}}, "消费者");customer.start();Thread producer = new Thread(() -> {Random random = new Random();while (true) {try {int num = random.nextInt(1000);System.out.println("生产元素: " + num);blockingQueue.put(num);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}, "生产者");producer.start();customer.join();producer.join();
}

13. time

java">public class ThreadTimer {public static void main(String[] args) {// 标准库的定时器.Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("时间到, 快起床!");}}, 3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("时间到2!");}}, 4000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("时间到3!");}}, 5000);System.out.println("开始计时!");}
}

14. 线程池

java">import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Demo26 {public static void main(String[] args) {ExecutorService pool = Executors.newCachedThreadPool();pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("这是任务");}});}
}

http://www.ppmy.cn/news/1455109.html

相关文章

WordPress Automatic插件 SQL注入漏洞复现(CVE-2024-27956)

0x01 产品简介 WordPress Automatic(又称为WP Automatic)是一款流行的WordPress插件,旨在帮助网站管理员自动化内容创建和发布。该插件可以从各种来源(如RSS Feeds、社交媒体、视频网站、新闻网站等)获取内容,并将其自动发布到WordPress网站。 0x02 漏洞概述 WordPres…

论文速递 | Operations Research 3月文章合集(下)

编者按 在本系列文章中&#xff0c;我们梳理了运筹学顶刊Operations Research在2024年3月份发布的18篇文章的基本信息&#xff0c;旨在帮助读者快速洞察领域新动态。本文为第二部分。 推荐文章1 ● 题目&#xff1a;An Unexpected Stochastic Dominance: Pareto Distributions…

升级了项目的部署方式,坑死我了!

大家好&#xff0c;我是程序员鱼皮。如标题所言&#xff0c;最近这两天&#xff0c;我对我们公司部分项目的部署方式进行了改造升级。 由于部署方式的调整可能会影响到线上用户的正常访问&#xff0c;所以只能挑在用户少的时间&#xff08;凌晨&#xff09;进行调整和测试。 …

霍尼韦尔HONEYWELL 16HM1-1 微型密封基本开关

微动开关的优点包括&#xff1a; 小巧灵活。由于其小型设计&#xff0c;微动开关易于安装在各种电子设备中&#xff0c;且能适应狭小的空间限制。 高精度触发。微动开关具有高精度的触发性能&#xff0c;操作可靠&#xff0c;避免误操作&#xff0c;且具有长寿命和耐用性。 …

Leetcode 108.将有序数组转换为二叉搜索树

题目描述 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被…

解决mac出现npm install 卡在“sill idealTree buildDeps“的问题

问题出现场景&#xff1a; 在新建一个项目尝试npm install命令时&#xff0c;一直卡在“sill idealTree buildDeps“ 尝试过的无效解决方案包括&#xff1a; 切换/关闭梯子重启更换网络更换npm源更新删除 package.json 最终解决方案&#xff1a; 引起问题的原因是MacOS设置中…

DataGrip2024的安装

DataGrip2024的安装 获取方法在最后&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;2024版本 下一步 确定安装路径 配置安装项 菜单目录 默认即可 安装结束 后续教程关注公众号&#xff1a;爬虫探索者&#xff0c;发送datagrip2024获取&#xff0c;安装方…

【LLM】动手部署个人知识库助手

文章目录 动手部署个人知识库助手环境依赖项目运行总结 动手部署个人知识库助手 经过前面章节的学习&#xff0c;理解了LLM、向量知识库等知识&#xff0c;本章节开始实践部署个人知识库助手。 本次部署的项目github地址个人知识库助手项目 环境依赖 技术资源要求 CPU: Int…