Java 多线程

1.多线程创建

方法1:通过 继承 thread 类

[子线程代码] MyThread.java

package example01_thread;

public class MyThread extends Thread {
@Override
public void run() {
// super.run();
for (int i = 0; i <= 1000; i++) {
System.out.println("===> 子线程 " + i);
}
}
}

[主线程代码] Test.java

package example01_thread;

public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
//myThread.run(); //方法调用,单线程。
myThread.start(); // 启动线程,多线程。 这个.statr() 继承自Thread类
for (int i = 0; i <= 1000; i++) {
System.out.println("===> 主线程 " + i);
}
}
}

[运行结果]

方法2:通过 实现 Runnable 接口

[子线程代码] MyRunnable.java

package example02_runnable;

public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 1000; i++) {
System.out.println("===> 子线程 " + i);
}
}
}

[主线程代码] Test2.java

package example02_runnable;

public class Test2 {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
for (int i = 0; i <= 1000; i++) {
System.out.println("===> 主线程 " + i);
}
}
}

[运行结果]

2.线程中的相关方法

(1)设置优先级 setPrlorty()

优先级可以用从1到10的范围指定。10表示最高优先级(Thread.MAX_PRIORITY),1表示最低优先级(Thread.MIN_PRIORITY),5是普通优先级(Thread.NORM_PRIORITY,默认)。

优先级不能超出1-10的取值范围,否则抛出IllegalArgumentException

效果受操作系统影响,可能存在无效情况,因此不要有业务逻辑依赖于线程优先级,结果会无法预期

  • Thread.setPriority()用来设定线程的优先级
  • 在线程 start 方法被调用之前,线程的优先级应该被设定
  • 线程的优先级不能超过所属线程组的优先级,即可以通过控制线程组的优先级,来控制线程组下线程的最大优先级
  • 线程的优先级未设定时,默认所属的线程组的优先级
  • 未指定线程优先级时,所有线程都携带普通优先级
  • 优先级最高的线程在执行时被给予优先,最终由 CPU 调度程序决定哪一个线程被执行
  • 与线程池中等待运行机会的线程相比,当前正在运行的线程可能总是拥有更高的优先级
  • 高优先级线程不一定先于低优先级的线程运行。

[子线程代码] MyThread3.java

package example03_priority;

public class MyThread3 extends  Thread{
public MyThread3(String name){
super.setName(name); //设置线程名字
}
@Override
public void run() {
//super.run();
for(int i = 0;i<=1000;i++){
System.out.println("===> 子线程 " +super.getName() + " "+ i);
}
}
}

[主线程代码] Test3.java (未设置线程优先级)

package example03_priority;

public class Test3 {
public static void main(String[] args) {
MyThread3 mt1 = new MyThread3("A");
MyThread3 mt2 = new MyThread3("B");
mt1.start();
mt2.start();
}
}

[运行结果]

[主线程代码] Test3.java (设置线程优先级)

package example03_priority;

public class Test3 {
public static void main(String[] args) {
MyThread3 mt1 = new MyThread3("A");
MyThread3 mt2 = new MyThread3("B");
mt1.setPriority(1);
mt2.setPriority(10);
mt1.start();
mt2.start();
}
}

[运行结果]

(2)设置睡眠 sleep()

sleep,每隔x时间 去执行一个y操作。参数单位:毫秒

package example04_sleep;
import java.text.SimpleDateFormat;
import java.util.Date; public class MyThread4 extends Thread{
@Override
public void run() {
//让子系统不停的显示系统当前时间
while(true){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = new Date();
System.out.println(sdf.format(d));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread4 mt = new MyThread4();
mt.start();
}
}

等待子线程执行完毕 join()

让主线程等待当前执行中子线程执行完毕。

[子线程代码]MyThread5.java

package example05_join;
public class MyThread5 extends Thread{
@Override
public void run() {
// super.run();
for(int i = 0;i<=1000;i++){
System.out.println("===> 子线程 " + i);
}
}
}

[主线程代码]Test5.java

package example05_join;

public class Test {
public static void main(String[] args) {
MyThread5 myThread5 = new MyThread5();
myThread5.start();
for(int i = 0;i<=100;i++){
System.out.println("===> 主线程 " + i);
}
try {
myThread5.join(); //此时让主线程等待子线程执行完毕,再继续执行
}catch (InterruptedException e){
e.printStackTrace();
} System.out.println("主线程执行完毕"); }
}

让出CPU资源,让其他线程执行 yield()

让行,不代表不执行。具体谁先进行,依然由CPU决定,不能完全确定

[子线程代码]MyThread6.java

package example06_yield;

public class MyThread6 extends Thread {
public MyThread6(String name) {
super.setName(name);
} @Override
public void run() {
// super.run();
for (int i = 0; i <= 500; i++) {
System.out.println(super.getName() + ":" + i);
if (i % 10 == 0) {
Thread.yield();
}
}
}
}

[主线程代码]Test6.java

package example06_yield;

import example04_sleep.yield.MyThread6;

public class Test6 {
public static void main(String[] args) {
MyThread6 mt1 = new MyThread6("A");
MyThread6 mt2 = new MyThread6("B");
mt1.start();
mt2.start();
}
}

[运行结果截图]

正如上图所见,让行是开发者设定的希望情况,但是并不代表一定成功让行。最终的决定权还是由CPU来决定

打断线程(尤其是正在睡眠中的线程) interrupt

[子线程代码]MyThread6.java

package example07.interrupt;

public class MyThread7  extends Thread{
@Override
public void run() {
// super.run();
System.out.println("线程即将进行休眠");
try{
Thread.sleep(1000000);
}catch (InterruptedException e){
// e.printStackTrace();
System.out.println("异常:线程休眠被打断");
}
System.out.println("线程被激活");
}
}

[主线程代码]Test6.java

package example07.interrupt;

public class Test7 {
public static void main(String[] args) {
MyThread7 myThread7 = new MyThread7();
myThread7.start();
for(int i = 0;i<=100;i++){
System.out.println("===> 主线程 " + i);
}
myThread7.interrupt(); // 打断正在休眠中的子线程
}
}

[运行结果截图]

3.线程同步

线程同步:当多个线程共享同一个资源的时候,我们可以在某一个线程访问到这个资源的时候,将这个资源暂时封锁

等待当前线程执行结束,解锁该资源,其他线程才可以来继续执行。

总结:等待其他线程释放锁

目的:让线程更加安全

下面以银行取钱案例说明。

[账户对象]Account.java

package example08_sync;

public class Account {
private double balance;
public Account(double balance){
this.balance = balance;
}
public void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
if(this.balance<=0){return;}
System.out.println("即将取走1000,目前金额为:"+this.balance);
this.balance -= 1000;
System.out.println("成功取走1000,目前金额为:"+this.balance);
}
}

[取钱线程]GetMoneyThread.java

package example08_sync;

public class GetMoneyThread extends Thread{
private Account acc;
public GetMoneyThread(Account acc){
this.acc = acc;
} @Override
public void run() {
acc.getMoney();
}
}

[测试类]Test8.java

package example08_sync;

public class Test8 {
public static void main(String[] args) {
//创建账户
Account account = new Account(1000);
//创建ATM线程
GetMoneyThread atm = new GetMoneyThread(account);
//创建柜台线程
GetMoneyThread table = new GetMoneyThread(account);
//取钱
atm.start();
table.start();
}
}

[运行结果]

可以发现,设置的余额不足拦截(if(this.balance<=0){return;})没有生效,出现了账户余额为负数情况。

为避免这些情况的出现,因此需要线程同步。即将多个处理统一资源的线程,进行队列化管理(锁)

实现线程同步的方法

方法1:在方法声明上添加一个synchronized关键字

package example08_sync;

public class Account {
private double balance;
public Account(double balance){
this.balance = balance;
}
public synchronized void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
if(this.balance<=0){return;}
System.out.println("即将取走1000,目前金额为:"+this.balance);
this.balance -= 1000;
System.out.println("成功取走1000,目前金额为:"+this.balance);
}
}

方法2:在方法内部添加一个synchronized关键字

package example08_sync;

public class Account {
private double balance;
public Account(double balance){
this.balance = balance;
}
public synchronized void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
synchronized (this){
if(this.balance<=0){return;}
System.out.println("即将取走1000,目前金额为:"+this.balance);
this.balance -= 1000;
System.out.println("成功取走1000,目前金额为:"+this.balance);
}
}
}

方法3:手动上锁

该方法用的最少,因为容易出现忘记解锁。

package example08_sync;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Account {
private double balance;
private Lock lock = new ReentrantLock(); //创建锁
public Account(double balance){
this.balance = balance;
}
public void getMoney(){ //在方法声明上添加synchronized关键字 一旦执行到该方法,瞬间锁定对象Account
lock.lock(); // 上锁
if(this.balance<=0){return;}
System.out.println("即将取走1000,目前金额为:"+this.balance);
this.balance -= 1000;
System.out.println("成功取走1000,目前金额为:"+this.balance);
lock.unlock(); //解锁
}
}

死锁

线程同步时最容易发生的问题

eg:线程A锁定资源1后,等待访问资源2;线程2锁定资源2后,等待访问资源1。

[资源对象] ResourceObjec.java

package example09_dead;

public class ResourceObject {
public static Object obj1 = new Object();
public static Object obj2 = new Object();
}

[线程1] DeadLock1.java

package example09_dead;

public class DeadLock1 extends Thread{
@Override
public void run() {
// super.run();
synchronized (ResourceObject.obj1){
System.out.println("线程1 第一个资源锁定");
try{
//通过休眠使CPU去执行其他线程
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (ResourceObject.obj2){
System.out.println("线程1 第二个资源锁定");
System.out.println("线程1执行完毕");
}
} }
}

[线程2] DeadLock2.java

package example09_dead;

public class DeadLock2 extends Thread{
@Override
public void run() {
// super.run();
synchronized (ResourceObject.obj2){
System.out.println("线程2 第二个资源锁定");
try{
//通过休眠使CPU去执行其他线程
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (ResourceObject.obj1){
System.out.println("线程2 第一个资源锁定");
System.out.println("线程2执行完毕");
}
}
}
}

[测试] Test9.java

package example09_dead;

public class Test9 {
public static void main(String[] args) {
DeadLock1 dl1 = new DeadLock1();
DeadLock2 dl2 = new DeadLock2();
dl1.start();
dl2.start();
}
}

[运行结果]

线程的生命周期

  • [1]创建线程
  • ---[start()]--> [2]就绪状态
  • ---[CPU调度]--> [3]运行状态
    • ---[stop()]--> (跳转到4)
    • ---[IO操作、sleep]--> 阻塞状态 ---[IO结束、sleep()结束]--> (跳转到2)
  • --------------> [4]消亡状态

生产者消费者模型

案例:实现对视频进行审查,如果是不良视频,则对视频进行删除

[视频对象] Video.java

package example10_check;

public class Video {
private String name;
public Video (String name){
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

[检查视频] CheckVideo.java

package example10_check;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger; public class CheckVideo extends Thread{
private static AtomicInteger i = new AtomicInteger(); //AtomicInteger原子型整数。特点:线程安全 效果等同于 int i = 0,但是比int i = 0 更安全。
//将视频添加到缓冲区(队列)
private BlockingQueue<Video> videos;
public CheckVideo(BlockingQueue<Video> videos){
this.videos = videos;
}
@Override
public void run() {
while (true){
String name = "不良视频"+i.incrementAndGet();// 相当于i++
Video v = new Video(name);
//将视频添加到队列
try {
System.out.println("发现不良视频"+name);
videos.put(v);
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} }
}
}

[删除视频] DelVideo.java

package example10_check;

import java.util.TreeMap;
import java.util.concurrent.BlockingQueue; public class DelVideo extends Thread {
private BlockingQueue<Video> videos;
public DelVideo(BlockingQueue<Video> videos){
this.videos = videos;
} @Override
public void run() {
while(true){
try{
Video video = videos.take();
System.out.println("删除不良视频"+video.getName());
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

[测试] Test10.java

package example10_check;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; public class Test10 {
public static void main(String[] args) {
BlockingQueue<Video> videos = new LinkedBlockingQueue<Video>();
//创建3个检查视频的线程
CheckVideo ck1 = new CheckVideo(videos);
CheckVideo ck2 = new CheckVideo(videos);
CheckVideo ck3 = new CheckVideo(videos);
//创建两个删除视频的线程
DelVideo d1 = new DelVideo(videos);
DelVideo d2 = new DelVideo(videos);
ck1.start();
ck2.start();
ck3.start();
d1.start();
d2.start(); }
}

[运行结果]

【Java高级编程】Java多线程学习笔记的更多相关文章

  1. 《Java并发编程实战》学习笔记 线程安全、共享对象和组合对象

    Java Concurrency in Practice,一本完美的Java并发参考手册. 查看豆瓣读书 推荐:InfoQ迷你书<Java并发编程的艺术> 第一章 介绍 线程的优势:充分利 ...

  2. 《Java并发编程实战》学习笔记

    第2章 线程安全性 正确性: 某个类的行为与其规范完全一致. 2.1线程安全: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或 ...

  3. 《Java并发编程实战》学习笔记 任务执行和取消关闭

    查看豆瓣读书 第六章 任务执行 大多数并发应用程序是围绕执行任务进行管理的.设计任务时,要为任务设计一个清晰的任务边界,并配合一个明确的任务执行策略.任务最好是独立的,因为这会提高并发度.大多数服务器 ...

  4. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  5. JAVA多线程学习笔记(1)

    JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...

  6. java进阶-多线程学习笔记

    多线程学习笔记 1.什么是线程 操作系统中 打开一个程序就是一个进程 一个进程可以创建多个线程 现在系统中 系统调度的最小单元是线程 2.多线程有什么用? 发挥多核CPU的优势 如果使用多线程 将计算 ...

  7. Java多线程学习笔记(一)——多线程实现和安全问题

    1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...

  8. 图灵学院JAVA互联网架构师专题学习笔记

    图灵学院JAVA互联网架构师专题学习笔记 下载链接:链接: https://pan.baidu.com/s/1xbxDzmnQudnYtMt5Ce1ONQ 密码: fbdj如果失效联系v:itit11 ...

  9. Java并发编程的艺术读书笔记(2)-并发编程模型

    title: Java并发编程的艺术读书笔记(2)-并发编程模型 date: 2017-05-05 23:37:20 tags: ['多线程','并发'] categories: 读书笔记 --- 1 ...

  10. Java并发编程的艺术读书笔记(1)-并发编程的挑战

    title: Java并发编程的艺术读书笔记(1)-并发编程的挑战 date: 2017-05-03 23:28:45 tags: ['多线程','并发'] categories: 读书笔记 --- ...

随机推荐

  1. 2023年5月中国数据库排行榜:OTO组合回归育新机,华为高斯蓄势待发展雄心

    路漫漫其修远兮,吾将上下而求索. 2023年5月的 墨天轮中国数据库流行度排行 火热出炉,本月共有262个数据库参与排名.本月排行榜前十变动较大,可以用一句话概括为:openGauss 立足创新夺探花 ...

  2. ChatGPT “眼”中的开源数据库

    开源作为数据库发展的未来趋势之一,被冠以"数据库弯道超车的法宝"的称号.中国开源数据库产品正处于蓬勃发展的趋势,根据 墨天轮中国数据库流行度 ,截止2023年2月底已有46款开源数 ...

  3. 墨天轮最受DBA欢迎的数据库技术文档-监控篇

    好久不见,<墨天轮最受欢迎的技术文档>系列文章回归啦!本期主题数据库监控篇,希望能够帮助到大家!此外,为感谢大家支持,原文文末也给大家带来了返场福利,欢迎大家进入原文参与~ 数据库监控是许 ...

  4. Vue的nextTick的原理

    知识储备:事件循环有宏任务和微任务,宏任务所处的队列就是宏任务队列,队列可以有多个,第一个队列只有一个任务就是执行主线程的js代码,剩余队列任务有setTimeout setInterval微任务所处 ...

  5. Oracle问题:alter update modify 的区别是什么?

    首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247486480&idx=1 ...

  6. 现代化 React UI 库:Material-UI 详解!

    随着 React 在前端开发中的流行,越来越多的 UI 框架和库开始涌现,以帮助开发者更高效地构建现代化.响应式的用户界面.其中,Material-UI 是基于 Google Material Des ...

  7. cmd杀死占用端口号的Java进程

    下面列出两种杀死进程的方法: 1.根据jps查询.2.根据端口号查询进程. 最后根据进程id杀死进程(注意:进程id不等同于端口号) 根据jps查进程 jps命令,列出Java进程列表 根据进程id杀 ...

  8. Kubernetes上安装nacos

    k8s配置 --- apiVersion: apps/v1 kind: Deployment metadata: name: nacos namespace: com spec: selector: ...

  9. 如何在离线的Linux服务器上部署 Ollama,并使用 Ollama 管理运行 Qwen 大模型

    手动安装 Ollama 根据Linux的版本下载对应版本的 Ollama, 查看Linux CPU型号,使用下面的命令 #查看Linux版本号 cat /proc/version #查看cpu架构 l ...

  10. 2.13 新手必读的Linux使用注意事项

    通过安装并体验 Linux 系统,读者应该能发现 Linux 与 Windows 的一些不同之处,本节就几个容易让初学者混淆的问题做重点讲解,以便加深读者对 Linux 系统的认识. Linux 严格 ...