https://www.nowcoder.com/tutorial/94/ae05554a3ad84e42b6f9fc4d52859dc4
https://how2j.cn/frontroute
https://how2j.cn/k/thread/thread-start/353.html
多进程
https://www.cnblogs.com/aademeng/articles/6141627.html
public class MultiProcessExample {
public static void main(String[] args) {
// 创建进程执行任务
ProcessBuilder processBuilder1 = new ProcessBuilder("java", "Task1");
ProcessBuilder processBuilder2 = new ProcessBuilder("java", "Task2");
try {
// 启动进程
Process process1 = processBuilder1.start();
Process process2 = processBuilder2.start();
// 等待进程执行结束
int exitCode1 = process1.waitFor();
int exitCode2 = process2.waitFor();
System.out.println("任务1执行结果:" + exitCode1);
System.out.println("任务2执行结果:" + exitCode2);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
class Task1 {
public static void main(String[] args) {
System.out.println("任务1开始执行");
// 执行具体任务...
System.out.println("任务1执行结束");
System.exit(0); // 退出进程并返回状态码
}
}
class Task2 {
public static void main(String[] args) {
System.out.println("任务2开始执行");
// 执行具体任务...
System.out.println("任务2执行结束");
System.exit(0); // 退出进程并返回状态码
}
}
线程
进程:系统资源分配的最小单位
线程:程序执行的最小单位
1 启动线程
3 个办法:
法1 继承 Thread 类
法2 实现 Runnable 接口
法3 匿名类
概念
进程(Processor)
线程(Thread)
启动 LOL.exe 叫进程
又启动 DOTA.exe,就是 2 个进程
【进程内部】是 【线程】
不用多线程
package 第18个程序_多线程.a1_概念.s1_不用多线程;
public class Hero {
public String name;
public float hp; // 血量
public int damage; // 攻击
public void attackHero(Hero hero){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
hero.hp -= damage;
System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);
if (isDead())
System.out.println(hero.name + " 阵亡!");
}
public boolean isDead(){
return hp>0? false:true;
}
}
package 第18个程序_多线程.a1_概念.s1_不用多线程;
public class test {
public static void main(String[] args) {
Hero g = new Hero();
g.name = "盖伦";
g.hp = 616;
g.damage = 50;
Hero t = new Hero();
t.name = "提莫";
t.hp = 300;
t.damage = 30;
Hero s = new Hero();
s.name = "赏金猎人";
s.hp = 500;
s.damage = 65;
Hero m = new Hero();
m.name = "盲僧";
m.hp = 455;
m.damage = 80;
System.out.println("盖伦攻击提莫!");
while (!t.isDead()){
g.attackHero(t);
}
System.out.println("\n赏金猎人攻击盲僧!");
while (!m.isDead()){
s.attackHero(m);
}
}
}

用多线程(法1)
不用多线程,就只能一个英雄攻击完另一个英雄,别的英雄才能攻击
用多线程:
package 第18个程序_多线程.a1_概念.s2_用多线程;
public class Hero {
public String name;
public float hp; // 血量
public int damage; // 攻击
public void attackHero(Hero hero){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
hero.hp -= damage;
System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);
if (isDead())
System.out.println(hero.name + " 阵亡!");
}
public boolean isDead(){
return hp>0? false:true;
}
}
package 第18个程序_多线程.a1_概念.s2_用多线程;
public class Kill extends Thread{
private Hero hero1;
private Hero hero2;
public Kill(Hero hero1, Hero hero2){
this.hero1 = hero1;
this.hero2 = hero2;
}
public void run(){
while (!hero2.isDead()){
hero1.attackHero(hero2);
}
}
}
package 第18个程序_多线程.a1_概念.s2_用多线程;
public class test {
public static void main(String[] args) {
Hero g = new Hero();
g.name = "盖伦";
g.hp = 616;
g.damage = 50;
Hero t = new Hero();
t.name = "提莫";
t.hp = 300;
t.damage = 30;
Hero s = new Hero();
s.name = "赏金猎人";
s.hp = 500;
s.damage = 65;
Hero m = new Hero();
m.name = "盲僧";
m.hp = 455;
m.damage = 80;
Kill kill1 = new Kill(g, t);
kill1.start();
Kill kill2 = new Kill(s, m);
kill2.start();
}
}

Runable(法2)
package 第18个程序_多线程.a2_Runnable;
public class Hero {
public String name;
public float hp; // 血量
public int damage; // 攻击
public void attackHero(Hero hero){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
hero.hp -= damage;
System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);
if (isDead())
System.out.println(hero.name + " 阵亡!");
}
public boolean isDead(){
return hp>0? false:true;
}
}
package 第18个程序_多线程.a2_Runnable;
public class battle implements Runnable{
private Hero hero1;
private Hero hero2;
public battle(Hero hero1, Hero hero2){
this.hero1 = hero1;
this.hero2 = hero2;
}
public void run(){
while (!hero2.isDead()){
hero1.attackHero(hero2);
}
}
}
package 第18个程序_多线程.a2_Runnable;
public class test {
public static void main(String[] args) {
Hero g = new Hero();
g.name = "盖伦";g.hp = 616;g.damage = 50;
Hero t = new Hero();
t.name = "提莫";t.hp = 300;t.damage = 30;
Hero s = new Hero();
s.name = "赏金猎人";s.hp = 500;s.damage = 65;
Hero m = new Hero();
m.name = "盲僧";m.hp = 455;m.damage = 80;
battle battle1 = new battle(g, t);
new Thread(battle1).start();
battle battle2 = new battle(s, m);
new Thread(battle2).start();;
}
}

多线程 - 匿名类(法3)
匿名类 继承 Thread,重写 run,直接在 run 中写业务代码
package 第18个程序_多线程.a3_多线程_匿名类;
public class Hero {
public String name;
public float hp; // 血量
public int damage; // 攻击
public void attackHero(Hero hero){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
hero.hp -= damage;
System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);
if (isDead())
System.out.println(hero.name + " 阵亡!");
}
public boolean isDead(){
return hp>0? false:true;
}
}
package 第18个程序_多线程.a3_多线程_匿名类;
public class test {
public static void main(String[] args) {
Hero g = new Hero();
g.name = "盖伦";g.hp = 616;g.damage = 50;
Hero t = new Hero();
t.name = "提莫";t.hp = 300;t.damage = 30;
Hero s = new Hero();
s.name = "赏金猎人";s.hp = 500;s.damage = 65;
Hero m = new Hero();
m.name = "盲僧";m.hp = 455;m.damage = 80;
//匿名类
Thread t1 = new Thread(){
public void run(){
while (!t.isDead()){
g.attackHero(t);
}
}
};
t1.start();
Thread t2 = new Thread(){
public void run(){
while (!m.isDead()){
s.attackHero(m);
}
}
};
t2.start();
}
}

练习
回顾 IO 综合练习
多线程查找文件内容
原练习思路:
遍历所有文件,当遍历到文件是 .java 时,查找其内容,查完后,再遍历下一个文件
多线程思路:
遍历所有文件,当遍历到文件是.java 时,创建线程查找其内容,不必等该线程结束,继续遍历下一个文件
主线程、子线程是并行的
用 join(),就会变成串行的
当主线程调用子线程 join() 时,子线程执行完后,主线程才开始执行
【上面这个】用于统计用时
不然统计不了,时间总输出于第一行
继承 Thread
package 第18个程序_多线程.练习.继承Thread;
import java.io.*;
public class ThreadInter extends Thread{
public File file;
public String mysearch;
public ThreadInter(File file, String mysearch){
this.file = file;
this.mysearch = mysearch;
}
public void run(){
char[] dates = new char[(int) file.length()];
try (
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
){
bufferedReader.read(dates);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (String.valueOf(dates).contains(mysearch)){
System.out.println("找到字符串在 " + file.getName() + " - " + file.getAbsolutePath());
}
}
}
package 第18个程序_多线程.练习.继承Thread;
import java.io.File;
public class test {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
String path = "src\\第18个程序_多线程\\练习\\src";
File file = new File(path);
if(!file.exists()){
System.out.println("该文件夹不存在!");
return;
}
search(file, "File");
long end = System.currentTimeMillis();
System.out.println("用时: " + (end - start) + " ms");
}
public static void search(File folder, String mysearch) throws InterruptedException {
File[] files = folder.listFiles();
for (File file:files){
if(file.isDirectory()){
search(file, mysearch);
}else if (file.isFile() && file.getName().endsWith(".java")){
ThreadInter threadInter = new ThreadInter(file, mysearch);
threadInter.start();
threadInter.join();
}
}
}
}

Runnable 实现
package 第18个程序_多线程.练习.Runnable方法;
import java.io.*;
public class RunnableInter implements Runnable{
public File file;
public String mysearch;
public RunnableInter(File file, String mysearch){
this.file = file;
this.mysearch = mysearch;
}
@Override
public void run() {
char[] dates = new char[(int) file.length()];
try (
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
){
bufferedReader.read(dates);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (String.valueOf(dates).contains(mysearch)){
System.out.println("找到字符串在 " + file.getName() + " - " + file.getAbsolutePath());
}
}
}
package 第18个程序_多线程.练习.Runnable方法;
import java.io.File;
public class test {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
String path = "src\\第18个程序_多线程\\练习\\src";
File file = new File(path);
if(!file.exists()){
System.out.println("该文件夹不存在!");
return;
}
search(file, "File");
long end = System.currentTimeMillis();
System.out.println("用时: " + (end - start) + " ms");
}
public static void search(File folder, String mysearch) throws InterruptedException {
File[] files = folder.listFiles();
for (File file:files){
if(file.isDirectory()){
search(file, mysearch);
}else if (file.isFile() && file.getName().endsWith(".java")){
RunnableInter runnableInter = new RunnableInter(file, mysearch);
Thread t = new Thread(runnableInter);
t.start();
t.join();
}
}
}
}

匿名类 实现
package 第18个程序_多线程.练习.匿名类方法;
import java.io.*;
import java.util.concurrent.CountDownLatch;
public class test {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
String path = "src\\第18个程序_多线程\\练习\\src";
File file = new File(path);
if(!file.exists()){
System.out.println("该文件夹不存在!");
return;
}
search(file,"File");
long end = System.currentTimeMillis();
System.out.println("用时: " + (end - start) + " ms");
}
public static void search(File folder, String mysearch) throws InterruptedException {
File[] files = folder.listFiles();
for (File file:files){
if(file.isDirectory()){
search(file, mysearch);
}else if (file.isFile() && file.getName().endsWith(".java")){
Thread t = new Thread(){
public void run(){
char[] datas = new char[(int) file.length()];
try(
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
){
bufferedReader.read(datas);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (String.valueOf(datas).contains(mysearch)){
System.out.println("找到字符串在 " + file.getName() + " - " + file.getAbsolutePath());
}
}
};
t.start();
t.join();
}
}
}
}

不用多线程查找,用时是 7ms,说明所线程在这里会用时变多
2 线程方法(常见)
sleep:当前线程暂停
join:加入到当前线程中
setPriority:线程优先级
yield:临时暂停
setDaemon:守护线程
notify():随机选一个在指定对象上挂起的线程激活
notifyAll():激活指定对象上挂起的所有线程
sleep
Thread.sleep(1000); :当前线程暂停 1000 毫秒(ms) ,其他线程不受影响
对应 InterruptedException 中断异常
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
join
main(){
...
//代码执行到这里,一直是main线程在运行
try {
//t1线程加入到 main 线程中来,只有t1线程运行结束,才继续往下走
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setPriority - 线程优先级
Thread t1= new Thread(){
public void run(){
while(!teemo.isDead()){
gareen.attackHero(teemo);
}
}
};
Thread t2= new Thread(){
public void run(){
while(!leesin.isDead()){
bh.attackHero(leesin);
}
}
};
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
yiel - 临时暂停
Thread.yield();
setDaemon - 守护线程
当进程中,所有线程都是守护线程时,结束当前进程
守护线程 常用来做日志,性能统计
例子:
公司有 销售部、生产部
还有后勤、行政
销售部,生产部都解散了,只剩下后勤、行政,这家公司也就解散了
public static void main(String[] args) {
Thread t1= new Thread(){
public void run(){
int seconds =0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("已经玩了LOL %d 秒%n", seconds++);
}
}
};
t1.setDaemon(true);
t1.start();
}
练习1
实现:
英雄可以放技能叫做: 波动拳 - a_du_gen
每隔一秒钟,可发一次,只能连续发 3 次
3 次后,要充能 5 秒,再继续发
package 第18个程序_多线程.a2_常用方法.练习1;
public class Hero {
public String name;
public float hp; // 血量
public int damage; // 攻击
public Hero(){}
public Hero(String name){
this.name = name;
}
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
public boolean isDead(){
return hp>0? false:true;
}
public void a_du_gen(){
while (true){
for (int i = 1; i < 4; i++){
try {
System.out.println(this.name + " 发动技能第 " + i + " 次, 需要充能 1 秒");
Thread.sleep(1000); // 充能1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.name + "3次攻击后要充能5秒");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package 第18个程序_多线程.a2_常用方法.练习1;
public class test {
public static void main(String[] args) {
Hero hero1 = new Hero("盖伦");
Hero hero2 = new Hero("提莫");
Thread thread1 = new Thread(){
public void run(){
hero1.a_du_gen();
}
};
Thread thread2 = new Thread(){
public void run(){
hero2.a_du_gen();
}
};
thread1.start();
thread2.start();
}
}

练习2
1 随机生成长度是 3 的字符串,作为密码
2 创建破解线程,用穷举法,匹配该密码
3 创建日志线程,打印都用过哪些字符串去匹配,这个日志线程为守护线程
package 第18个程序_多线程.a2_常用方法.练习2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class test {
public static void main(String[] args) throws InterruptedException {
char[] c = new char[3];
for (int i = 0; i < 3; i++){
c[i] = (char) ((int)(Math.random()*26)+65);
}
String s = Arrays.toString(c);
System.out.println("密码是: " + s + "\n");
List<String> list = new ArrayList<>();
Thread thread = new Thread(){
public void run(){
char[] temp = new char[3];
int flag = 0;
for (char i = 'A'; i <= 'Z'; i++){
temp[0] = i;
for (char j = 'A'; j <= 'Z'; j++){
temp[1] = j;
for(char z = 'A'; z <= 'Z'; z++){
temp[2] = z;
String s1 = Arrays.toString(temp);
list.add(s1);
if(s1.equals(s)){
flag = 1;
System.out.println("找到了密码 : " + s1 + "\n");
}
}
if(flag == 1)
break;
}
if (flag == 1)
break;
}
}
};
Thread log = new Thread(){
public void run(){
while (true){
if (!list.isEmpty()){
System.out.println("记录过的密码: " + list.remove(0));
}
else Thread.yield();
}
}
};
thread.start();
thread.join();
log.setDaemon(true);
log.start();
log.join();
}
}

3 同步(线程安全)
对个数据进行多次加减操作。每次加减 1
如,10000,多次加减,加减有多个线程
加1时得到 10001,此时减得到的是10000,10000-1=9999
这个值就变成了 9999,本来一加一减还是 10000 本身才对
这个 9999 就是【脏数据】
解决办法:
【加线程】运行时是并行运算,【减线程】不被允许访问数据
【减线程】运行时是并行运算,【加线程】不被允许访问数据
synchronized
Object someObject =new Object();
synchronized (someObject){
// 此处的代码只有占有了 someObject 才能执行
}
盖伦有 10000 滴血
同时被对方多个英雄攻击 = 有多个线程在减少盖伦的 hp
在基地 = 有多个线程在恢复盖伦的 hp
package 第18个程序_多线程.a3_同步.s1_加减血量;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
public void recover(){
hp++;
}
// 掉血
public void hurt(){
hp--;
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a3_同步.s1_加减血量;
public class test {
public static void main(String[] args) {
long start = System.currentTimeMillis();
Hero g = new Hero("盖伦", 10000);
System.out.println(g.name + " 初始血量 = " + g.hp + "\n");
int n = 10000;
Thread[] addthread = new Thread[n];
Thread[] reducethread = new Thread[n];
final Object someObject = new Object();
for(int i = 0; i < n; i++){
Thread t1 = new Thread(){
public void run(){
synchronized (someObject){
g.recover();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
addthread[i]= t1;
}
for(int i = 0; i < n; i++){
Thread t2 = new Thread(){
public void run(){
synchronized (someObject){
g.hurt();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t2.start();
reducethread[i]= t2;
}
// 等待所有的加线程结束
for (Thread t : addthread){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 等待所有的减线程结束
for (Thread t : reducethread){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(n + " 个【加线程】和 " + n + " 个【减线程】结束后\n" + g.name + " 血量 = " + g.hp);
long end= System.currentTimeMillis();
System.out.println("用时 = " + (end - start) + "ms");
}
}

Hero 作为对象
public void hurt(){
//使用this作为同步对象
synchronized (this) {
hp=hp-1;
}
}
完整代码:
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
public void recover(){
hp++;
}
// 掉血
public void hurt(){
synchronized (this){
hp--;
}
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象;
public class test {
public static void main(String[] args) {
long start = System.currentTimeMillis();
Hero g = new Hero("盖伦", 10000);
System.out.println(g.name + " 初始血量 = " + g.hp + "\n");
int n = 10000;
Thread[] addthread = new Thread[n];
Thread[] reducethread = new Thread[n];
final Object someObject = new Object();
for(int i = 0; i < n; i++){
Thread t1 = new Thread(){
public void run(){
synchronized (g){
g.recover();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
addthread[i]= t1;
}
for(int i = 0; i < n; i++){
Thread t2 = new Thread(){
public void run(){
g.hurt();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t2.start();
reducethread[i]= t2;
}
// 等待所有的加线程结束
for (Thread t : addthread){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 等待所有的减线程结束
for (Thread t : reducethread){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(n + " 个【加线程】和 " + n + " 个【减线程】结束后\n" + g.name + " 血量 = " + g.hp);
long end= System.currentTimeMillis();
System.out.println("用时 = " + (end - start) + "ms");
}
}

相同效果的其他做法
直接在方法前加上修饰符 synchronized
其所对应的同步对象,就是 this
和 hurt 方法达到的效果一样
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象.c2_相同效果_方法名前加synchronized;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
// 效果用下面的 【掉血】的以Hero为同步对象 一样!
public synchronized void recover(){
hp++;
}
// 掉血
public void hurt(){
synchronized (this){
hp--;
}
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象.c2_相同效果_方法名前加synchronized;
public class test {
public static void main(String[] args) {
long start = System.currentTimeMillis();
Hero g = new Hero("盖伦", 10000);
System.out.println(g.name + " 初始血量 = " + g.hp + "\n");
int n = 10000;
Thread[] addthread = new Thread[n];
Thread[] reducethread = new Thread[n];
final Object someObject = new Object();
for(int i = 0; i < n; i++){
Thread t1 = new Thread(){
public void run(){
g.recover();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
addthread[i]= t1;
}
for(int i = 0; i < n; i++){
Thread t2 = new Thread(){
public void run(){
g.hurt();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t2.start();
reducethread[i]= t2;
}
// 等待所有的加线程结束
for (Thread t : addthread){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 等待所有的减线程结束
for (Thread t : reducethread){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(n + " 个【加线程】和 " + n + " 个【减线程】结束后\n" + g.name + " 血量 = " + g.hp);
long end= System.currentTimeMillis();
System.out.println("用时 = " + (end - start) + "ms");
}
}

练习
MyStack 类,改为线程安全的类
一个类,其方法都是 synchronized 修饰,该类就是做线程安全的类
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;
public class Hero {
public String name; // 名字
public float hp; // 护甲
public int damage; // 攻击力
public Hero(){}
public Hero(String name){
this.name = name;
}
public synchronized String toString(){
return name;
}
}
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;
public interface stack {
//把英雄推入到最后位置
public void push(Hero h);
//把最后一个英雄取出来
public Hero pull();
//查看最后一个英雄
public Hero peek();
}
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;
import java.util.Iterator;
import java.util.LinkedList;
public class MyStack implements stack {
private static LinkedList<Hero> list = new LinkedList<>();
//把英雄推入到最后位置
@Override
public synchronized void push(Hero h) {
list.addLast(h);
}
//把最后一个英雄取出来
@Override
public Hero pull() {
return list.removeLast();
}
//查看最后一个英雄
@Override
public Hero peek() {
return list.peekLast();
}
public synchronized void get(){
Iterator<Hero> it= list.iterator();
System.out.println("英雄有:");
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println();
}
}
package 第18个程序_多线程.a3_同步.s2_Hero作为同步对象.栈改为线程安全类.我的答案;
public class test {
public static void main(String[] args) {
MyStack myStack = new MyStack();
myStack.push(new Hero("盖伦"));
myStack.push(new Hero("提莫"));
myStack.get();
System.out.println("把最后一个英雄取出来:");
System.out.println(myStack.pull());
myStack.get();
System.out.println("查看最后一个英雄:");
System.out.println(myStack.peek());
myStack.get();
}
}

4 线程安全
线程安全:对象对应的公共数据区始终正确
HashMap vs Hashtable
HashMap、Hashtable 都实现 Map 接口,都是 键值对保存数据的方式
区别1:
HashMap 可以放 null
Hashtable 不可放 null
区别2:
HashMap 不是线程安全类
Hashtable 是线程安全类

StringBuffer vs StringBuilder
StringBuffer 线程安全
StringBuilder 非线程安全
大量字符串拼接操作时,若是单线程 用 StringBuilder 更快
若是多线程,用 StringBuffer 保证数据安全
Vector vs ArrayList
Vector 线程安全
ArrayList 非线程安全
非线程安全的集合 转 为线程安全 synchronizedList
ArrayList 非线程安全 = 多个线程可同时进入一个 ArrayList.add 方法
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = Collections.synchronizedList(list1);
}
练习
MyStack 类,改为线程安全的类
package 第18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;
public class Hero {
public String name; // 名字
public float hp; // 护甲
public int damage; // 攻击力
public Hero(){}
public Hero(String name){
this.name = name;
}
public String toString(){
return name;
}
}
package 第18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;
public interface stack {
//把英雄推入到最后位置
public void push(Hero h);
//把最后一个英雄取出来
public Hero pull();
//查看最后一个英雄
public Hero peek();
}
package 第18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;
import java.util.*;
public class MyStack<T>implements stack{
private static List<Hero> list = Collections.synchronizedList(new LinkedList<Hero>());
//把英雄推入到最后位置
@Override
public void push(Hero h) {
list.add(h);
}
//把最后一个英雄取出来
@Override
public Hero pull() {
return list.remove(list.size() - 1);
}
//查看最后一个英雄
@Override
public Hero peek() {
return list.get(list.size()-1);
}
public void get() {
Iterator<Hero> it = list.iterator();
System.out.println("英雄有:");
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println();
}
}
package 第18个程序_多线程.a4_线程安全.栈改为线程安全类.我的答案;
public class test {
public static void main(String[] args) {
MyStack myStack = new MyStack();
myStack.push(new Hero("盖伦"));
myStack.push(new Hero("提莫"));
myStack.get();
System.out.println("把最后一个英雄取出来:");
System.out.println(myStack.pull());
myStack.get();
System.out.println("查看最后一个英雄:");
System.out.println(myStack.peek());
myStack.get();
}
}

练习
两种方式
implements Runnable
extends Thread
public class Threadtest {
public static void main(String[] args) { //主方法
//创建对象c和c1
Compute c = new Compute();
Compute1 c1 = new Compute1();
//创建线程对象t和t1
Thread t = new Thread(c);
Thread t1 = new Thread(c1);
t.start(); //启动线程对象t
t1.start(); //启动线程对象t1
}
}
class Compute1 implements Runnable{
public void run() { //实现方法run()
for (int i = 0; i < 10; i++) {
System.out.println("这个数字是:" + i);
}
}
}
class Compute implements Runnable{
int i = 0; //创建成员变量i
public void run() { //实现方法run()
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
public class Threadtest1 {
public static void main(String[] args) { //主方法
//创建对象t和t1
Compute2 t = new Compute2();
Compute3 t1 = new Compute3();
//启动对象t和t1
t.start();
t1.start();
}
}
class Compute2 extends Thread { //创建继承线程的类cCompute
int i = 0; //创建成员变量i
public void run() { //实现方法run()
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
class Compute3 extends Thread { //创建继承线程的类cCompute1
public void run() { //实现方法run()
for (int i = 0; i < 10; i++) {
System.out.println("这个数字是:" + i);
}
}
}
implements Runnable好处:适合多个相同程序代码的线程处理同一资源
卖火车票: implements Runnable
public class Example {
public static void main(String[] args) {
TicketWindow tw = new TicketWindow();
new Thread(tw, "窗口1").start(); //创建线程对象并命名窗口1,开启进程
new Thread(tw, "窗口2").start();
new Thread(tw, "窗口3").start();
new Thread(tw, "窗口4").start();
}
}
class TicketWindow implements Runnable {
private int tickets = 100;
public void run() {
while(true) {
if(tickets > 0) {
Thread th = Thread.currentThread(); //获取当前进程
String th_name = th.getName();
System.out.println(th_name+"正在发售第"+tickets--"张票");
}
System.out.println("MyThread类的run方法在运行");
}
}
}
其他方法

线程设置优先级:
线程名.setPriority(MIN_PRIORITY)
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
sleep(1000); 线程休眠1秒
t.interrupt(); 线程唤醒
yield(); 线程暂停,不会阻塞线程,只是转换为就绪状态
join() 线程进入阻塞状态,例如在线程B中调用线程A的join(),那线程B会进入到阻塞队列,直到线程A结束或中断线程
synchronized 同步
同步块:
Object lock = new Object();
synchronized(lock)
{}
同步方法:
public synchronized int make(){}
package ch12code;
public class st {
private int[] data = new int[10];
private int ip=0, op=0, c=0;
public synchronized void put(int n){
try {
while (c == data.length) {
this.wait();
}
data[ip] = n;
System.out.println(ip + " 放入 " + n);
ip++;
if(ip == data.length) {
ip = 0;
}
c++;
this.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void get(){
try {
while (c==0){
this.wait();
}
int t = data[op];
System.out.println(op + " 取出 " + t);
data[op] = 0;
op++;
if(op==data.length){
op = 0;
}
c--;
this.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class input implements Runnable{
private st s;
private int num=0;
input(st s){
this.s = s;
}
@Override
public void run() {
while (num<20){
s.put(num++);
}
}
}
class output implements Runnable{
private st s;
output(st s){
this.s = s;
}
@Override
public void run() {
while (true){
s.get();
}
}
}
class test{
public static void main(String[] args) {
st s = new st();
input s1 = new input(s);
output s2 = new output(s);
new Thread(s1).start();
new Thread(s2).start();
}
}
实践
class ham1{
static Object box = new Object(); //创建对象box
static int totalmaterial = 10; //制作汉堡包的材料属性
static int sales1 = 0; //销售多少个汉堡包属性
static int sales2=0;
static int production = 5; //一共多少汉堡
}
//厨师
class hmaker1 extends Thread {
//make方法使用了一个同步块,在这个函数里会不断地生产汉堡包
public void make() {
synchronized (ham1.box) { //创建同步块
(ham1.production)++;
System.out.println("厨师" + getName() + ":" + "汉堡包来了(总共" + (ham1.production - ham1.sales1 - ham1.sales2) + "个)");
try {
ham1.box.notify();
} catch (Exception e) {
}
}
}
public void run() {
//使用循环语句来保证在汉堡包材料用完之前,不断地生产汉堡包
while (ham1.production < ham1.totalmaterial) {
make();
try {
sleep(3000); //线程休眠3秒
} catch (Exception e) {
}
make(); //调用make()方法
}
}
}
// 营业员
class hassistant1 extends Thread{
public void sell1() { //创建营业员卖汉堡包的方法sell1
//当没有汉堡包的时候
if (ham1.production == (ham1.sales1 + ham1.sales2)) {
System.out.println("营业员" + getName() + ":顾客朋友们,请稍微等一下,汉堡包没了!!");
ham1.sales1 = 0;
ham1.production = 0;
try {
ham1.box.wait();
} catch (Exception e) {
}
}
(ham1.sales1)++;
//输出相应信息
System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham1.sales1+ "个)");
}
public void sell2() { //创建营业员卖汉堡包的方法sell2
//当没有汉堡包的时候
if (ham1.production == (ham1.sales1 + ham1.sales2)) {
System.out.println("营业员" + getName() + ":顾客朋友们,请稍微等一下,汉堡包没了!!");
ham1.sales2 = 0;
ham1.production = 0;
try {
ham1.box.wait();
} catch (Exception e) {
}
}
(ham1.sales2)++;
//输出相应信息
System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham1.sales2+ "个)");
}
public void run() { //重写run()方法
//当箱子里面有汉堡包里有汉堡包的情况下不断的卖
while ((ham1.sales1 + ham1.sales2) < ham1.production) {
sell1(); //调用sell1()方法
try {
sleep(1000);
} catch (Exception e) {
}
}
while ((ham1.sales1 + ham1.sales2) < ham1.production) {
try {
sleep(1000);
} catch (Exception e) {
}
sell2(); //调用方法sell2()
}
}
}
public class t2 {
public static void main(String[] args) {
hmaker1 maker = new hmaker1(); //创建对象maker
hassistant1 assistant1 = new hassistant1();//创建对象assistant
hassistant1 assistant2 = new hassistant1();//创建对象assistant
//对对象maker进行设置
maker.setName("甲");
//启动线程
assistant1.setName("甲");
assistant2.setName("乙");
maker.start();
assistant1.start();
assistant2.start();
}
}
//汉堡
class ham2{
static Object box1 = new Object(); //创建对象box
static Object box2 = new Object(); //创建对象box
static int totalmaterial1 = 10; //制作汉堡包的材料属性
static int totalmaterial2 = 10; //制作汉堡包的材料属性
static int sales11 = 0; //销售多少个汉堡包属性
static int sales12 =0;
static int sales21 =0;
static int sales22 =0;
static int production1 = 5; //一共多少汉堡
static int production2 = 5; //一共多少汉堡
}
//厨师
class hmaker2 extends Thread {
//make方法使用了一个同步块,在这个函数里会不断地生产汉堡包
public void make() {
synchronized (ham2.box1) { //创建同步块
(ham2.production1)++;
//输出相应的信息
System.out.println("厨师" + getName() + ":" + "汉堡包来了(总共" + (ham2.production1 - ham2.sales11 - ham2.sales12) + "个A类汉堡包)");
try {
ham2.box1.notify();
} catch (Exception e) {
}
}
}
public void run() { //重写run()方法
//使用循环语句来保证在汉堡包材料用完之前,不断地生产汉堡包
while (ham2.production1 < ham2.totalmaterial1) {
make(); //调用make()方法
try {
sleep(3000); //线程休眠3秒
} catch (Exception e) {
}
if (ham2.production1 == ham2.totalmaterial1) {
System.out.println("所有的材料用完了!");
}
}
}
}
class hmaker3 extends Thread {
//make方法使用了一个同步块,在这个函数里会不断地生产汉堡包
public void make() {
synchronized (ham2.box2) { //创建同步块
(ham2.production2)++;
//输出相应的信息
System.out.println("厨师" + getName() + ":" + "汉堡包来了(总共" + (ham2.production2 - ham2.sales21 - ham2.sales22) + "个B类汉堡包)");
try {
ham2.box2.notify();
} catch (Exception e) {
}
}
}
public void run() { //重写run()方法
//使用循环语句来保证在汉堡包材料用完之前,不断地生产汉堡包
while (ham2.production2 < ham2.totalmaterial2) {
make(); //调用make()方法
try {
sleep(3000); //线程休眠3秒
} catch (Exception e) {
}
if (ham2.production2 == ham2.totalmaterial2) {
System.out.println("所有的材料用完了!");
}
}
}
}
// 营业员
class hassistant2 extends Thread{
public void sell1() { //创建营业员卖汉堡包的方法sell1
//当没有汉堡包的时候
if (ham2.production1 == (ham2.sales11 + ham2.sales12)) {
System.out.println("营业员" + getName() + "asd:顾客朋友们,请稍微等一下,A汉堡包没了!!");
ham2.sales11 = ham2.sales12 = 0;
ham2.production1 = 0;
try {
ham2.box1.wait();
} catch (Exception e) {
}
}else {
if (ham2.production1 > (ham2.sales11 + ham2.sales12)) {
ham2.sales11++;
ham2.sales21++;
//输出相应信息
System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham2.sales11 + "个A汉堡 " + ham2.sales21 + "个B汉堡)");
}
}
}
public void sell2() { //创建营业员卖汉堡包的方法sell2
//当没有汉堡包的时候
if (ham2.production2 == (ham2.sales21 + ham2.sales22)) {
System.out.println("营业员" + getName() + ":顾客朋友们,请稍微等一下,B汉堡包没了!!");
ham2.sales21 = ham2.sales22 = 0;
ham2.production2 = 0;
try {
ham2.box2.wait();
} catch (Exception e) {
}
}else {
if (ham2.production2 > (ham2.sales21 + ham2.sales22)) {
ham2.sales12++;
ham2.sales22++;
//输出相应信息
System.out.println("营业员" + getName() + ":顾客好,汉堡包上来了,(总共卖了" + ham2.sales12 + "个A汉堡 " + ham2.sales22 + "个B汉堡)");
}
}
}
public void run() { //重写run()方法
//当箱子里面有汉堡包里有汉堡包的情况下不断的卖
while ((ham2.sales11 + ham2.sales12) < ham2.production1) {
try {
sleep(1000);
} catch (Exception e) {
}
sell1(); //调用sell1()方法
}
while ((ham2.sales21 + ham2.sales22) < ham2.production2) {
try {
sleep(1000);
} catch (Exception e) {
}
sell2(); //调用方法sell2()
}
}
}
public class t4 {
public static void main(String[] args) {
hmaker2 maker1 = new hmaker2(); //创建对象maker
hmaker3 maker2 = new hmaker3(); //创建对象maker
hassistant2 assistant1 = new hassistant2();//创建对象assistant
hassistant2 assistant2 = new hassistant2();//创建对象assistant
//对对象maker进行设置
maker1.setName("甲");
maker2.setName("乙");
assistant1.setName("甲");
assistant2.setName("乙");
//启动线程
maker1.start();
maker2.start();
assistant1.start();
assistant2.start();
}
}
5 死锁
线程1 已占有对象1,试图占有对象2
线程2 已占有对象2,试图占有对象1
线程1 等 线程2 释放对象2
线程2 等 线程1 释放对象1
简单程序
package 第18个程序_多线程.a5_死锁.s1_简单程序;
public class Hero {
public String name;
public float hp; // 血量
public int damage; // 攻击
public void attackHero(Hero hero){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
hero.hp -= damage;
System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);
if (isDead())
System.out.println(hero.name + " 阵亡!");
}
public Hero(String name){
this.name= name;
}
public boolean isDead(){
return hp>0? false:true;
}
}
package 第18个程序_多线程.a5_死锁.s1_简单程序;
public class test {
public static void main(String[] args) {
final Hero j = new Hero("九尾妖狐");
final Hero a = new Hero("安妮");
Thread t1 = new Thread(){
public void run(){
synchronized (j){
System.out.println("t1 已占有九尾妖狐");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 尝试占有 安妮...");
System.out.println("等待中...");
synchronized (a){
System.out.println("do something");
}
}
}
};
t1.start();
Thread t2 = new Thread(){
public void run(){
synchronized (a){
System.out.println("t2 已占有安妮");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 尝试占有 九尾妖狐...");
System.out.println("等待中...");
synchronized (j){
System.out.println("do something");
}
}
}
};
t2.start();
}
}

练习
3 个同步对象 a, b, c
3 个线程 t1, t2, t3
设计 使这 3 个线程彼此死锁
package 第18个程序_多线程.a5_死锁.练习;
public class Hero {
public String name;
public float hp; // 血量
public int damage; // 攻击
public void attackHero(Hero hero){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
hero.hp -= damage;
System.out.printf("%s 正在攻击 %s, %s 的血量变成了 %.2f\n", name, hero.name, hero.name, hero.hp);
if (isDead())
System.out.println(hero.name + " 阵亡!");
}
public Hero(String name){
this.name= name;
}
public boolean isDead(){
return hp>0? false:true;
}
}
package 第18个程序_多线程.a5_死锁.练习;
public class test {
public static void main(String[] args) {
final Hero j = new Hero("九尾妖狐");
final Hero m = new Hero("盲僧");
final Hero t = new Hero("提莫");
Thread t1 = new Thread(){
public void run() {
synchronized (j){
System.out.println("t1 已占有九尾妖狐");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 尝试占有 盲僧...");
System.out.println("等待中...");
synchronized (m){
System.out.println("do something");
}
}
}
};
t1.start();
Thread t2 = new Thread(){
public void run() {
synchronized (m){
System.out.println("t2 已占有盲僧");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2 尝试占有 提莫...");
System.out.println("等待中...");
synchronized (t){
System.out.println("do something");
}
}
}
};
t2.start();
Thread t3 = new Thread(){
public void run() {
synchronized (m){
System.out.println("t3 已占有提莫");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t3 尝试占有 九尾妖狐...");
System.out.println("等待中...");
synchronized (j){
System.out.println("do something");
}
}
}
};
t3.start();
}
}

6 交互
线程间有交互通知的需求
如: 2 个线程,处理同一个英雄,加血、减血
减血线程,发现血量=1,就停止减血,直到加血的线程为英雄加血,才能继续减血
反例
不好的做法:
用 while 故意设计减血线程频率更高
package 第18个程序_多线程.a6_线程交互.s1_反例;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
public void recover(){
hp++;
}
// 掉血
public void hurt(){
hp--;
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a6_线程交互.s1_反例;
public class test {
public static void main(String[] args) {
final Hero g = new Hero("盖伦",10);
Thread t1 = new Thread(){
public void run(){
while (true){
while (g.hp == 1){
continue;
}
g.hurt();
System.out.println(g.name + "的血量减1, 现血量 = " + g.hp);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
Thread t2 = new Thread(){
public void run() {
while (true){
g.recover();
System.out.println(g.name + "的血量加1, 现血量 = " + g.hp);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t2.start();
}
}

wait、notify
this.wait():占有 this 的线程等待,并临时释放占有
this.notify():通知等待 this 的线程,可以苏醒了
this.notifyAll():所有的等待 this 的线程,你们可以苏醒了
this 可以换成其他对象
wait()、notify(),不是 Thread 线程的方法,而是 对象 Object 的方法
所有的 Object 都可以作为同步对象
package 第18个程序_多线程.a6_线程交互.wait_notify;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
public synchronized void recover(){
hp++;
System.out.println(name + "的血量加1, 现血量 = " + hp);
this.notify();// 等待this的线程可以苏醒了
}
// 掉血
public synchronized void hurt(){
if (hp == 1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
hp--;
System.out.println(name + "的血量减1, 现血量 = " + hp);
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a6_线程交互.wait_notify;
public class test {
public static void main(String[] args) {
final Hero g = new Hero("盖伦",10);
Thread t1 = new Thread(){
public void run(){
while (true){
g.hurt();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
Thread t2 = new Thread(){
public void run() {
while (true){
g.recover();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t2.start();
}
}

练习1 - 线程交互
加血线程更频繁,英雄的最大血量1000
设计加血、减血线程的交互,
回血回满后,加血线程等待,直到有减血线程减血
package 第18个程序_多线程.a6_线程交互.练习1_线程交互;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
public synchronized void recover(){
if (hp == 1000){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
hp++;
System.out.println(name + "的血量加1, 现血量 = " + hp);
}
// 掉血
public synchronized void hurt(){
hp--;
System.out.println(name + "的血量减1, 现血量 = " + hp);
this.notify();
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a6_线程交互.练习1_线程交互;
public class test {
public static void main(String[] args) {
final Hero g = new Hero("盖伦",940);
Thread t1 = new Thread(){
public void run(){
while (true){
g.hurt();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
Thread t2 = new Thread(){
public void run(){
while (true){
g.recover();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t2.start();
}
}

练习2 - 多线程交互
加血线程 2 个,减血线程 5 个,同时运行
运行一段时间,观察发生的错误,分析原因,考虑解决办法
package 第18个程序_多线程.a6_线程交互.练习2_多线程交互;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
public synchronized void recover(){
if (hp == 1000){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
hp++;
System.out.println(name + "的血量加1, 现血量 = " + hp);
}
// 掉血
public synchronized void hurt(){
hp--;
System.out.println(name + "的血量减1, 现血量 = " + hp);
this.notify();
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a6_线程交互.练习2_多线程交互;
import java.util.ArrayList;
import java.util.List;
public class test {
public static void main(String[] args) {
final Hero g = new Hero("盖伦",940);
List<Thread> list1 = new ArrayList<>();
List<Thread> list2 = new ArrayList<>();
for (int i = 0; i < 2; i++){
Thread t1 = new Thread(){
public void run(){
while (true){
g.hurt();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
list1.add(t1);
}
for (int i = 0; i < 5; i++){
Thread t2 = new Thread(){
public void run(){
while (true){
g.recover();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
list2.add(t2);
}
for (Thread t1 : list1){
t1.start();
// t1.join();
}
for (Thread t2 : list2){
t2.start();
// t2.join();
}
}
}
问题是:
血量会超过 1000!

不好的解决办法:
把 Hero.hurt() 方法中,this.notify(); 注释掉

这样加血线程都停掉,最后血量变成负数
真正的解决办法:
package 第18个程序_多线程.a6_线程交互.练习2_多线程交互.改进;
public class Hero {
public String name;
public float hp; // 血量
public int damage;
public Hero(String name, float hp){
this.name = name;
this.hp = hp;
}
// 回血
public synchronized void recover(){
if (hp >= 1000){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
hp++;
System.out.println(name + "的血量加1, 现血量 = " + hp);
// this.notify();
}
// 掉血
public synchronized void hurt(){
if (hp <= 1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
hp--;
System.out.println(name + "的血量减1, 现血量 = " + hp);
}
public void attackHero(Hero hero){
hero.hp -= damage;
System.out.println(this.name + " 正在攻击 " + hero.name + ", " + hero.name + " 的血变成了 " + hero.hp);
if(hero.isDead()){
System.out.println(hero.name + "阵亡!");
}
}
public boolean isDead(){
return hp>0?false:true;
}
}
package 第18个程序_多线程.a6_线程交互.练习2_多线程交互.改进;
import java.util.ArrayList;
import java.util.List;
public class test {
public static void main(String[] args) {
final Hero g = new Hero("盖伦",940);
List<Thread> list1 = new ArrayList<>();
List<Thread> list2 = new ArrayList<>();
for (int i = 0; i < 5; i++){
Thread t1 = new Thread(){
public void run(){
while (true){
g.hurt();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
list1.add(t1);
}
for (int i = 0; i < 2; i++){
Thread t2 = new Thread(){
public void run(){
while (true){
g.recover();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
list2.add(t2);
}
for (Thread t1 : list1){
t1.start();
// t1.join();
}
for (Thread t2 : list2){
t2.start();
// t2.join();
}
}
}
练习3 - 生产者、消费者
1 用栈存放数据
1.1 栈是线程安全
1.2 栈里的数据是 0 时,访问 pull 的线程等待
栈里的数据有 200 个时,访问 push 的线程等待
2 生产者(Producer)线程类,生产【随机大写字符】压入栈
3 消费者(Consumer)线程类,从堆栈中弹出字符并打印到控制台
4 测试类,使 2 个生产者、3 个消费者线程同时运行,结果类似如下 :

package 第18个程序_多线程.a6_线程交互.练习3_生产者_消费者;
public interface stack {
public void push(char c); // 入栈
public char peek(); // 查看栈顶元素
public char pop(); // 出栈
public int size(); // 返回栈的元素个数
}
package 第18个程序_多线程.a6_线程交互.练习3_生产者_消费者;
import java.util.EmptyStackException;
import java.util.Vector;
public class MyStack implements stack{
Vector<Character> vector = new Vector<Character>(); // Vector 是线程安全
@Override // 入栈
public synchronized void push(char c) {
vector.addElement(c);
}
@Override // 查看栈顶元素
public synchronized char peek() {
int size = vector.size();
if (size == 0)
throw new EmptyStackException();
return vector.get(size-1);
}
@Override
public synchronized char pop() {
int size = vector.size();
char c = peek();
vector.removeElement(size-1);
return c;
}
@Override
public synchronized int size() {
return vector.size();
}
}
package 第18个程序_多线程.a6_线程交互.练习3_生产者_消费者;
import java.util.Random;
public class Producer extends Thread{
MyStack stack = new MyStack();
String name;
public Producer(){}
public Producer(String name){
this.name = name;
}
public synchronized void run(){
Random r = new Random();
char t;
while(true){
t = (char) (r.nextInt(27) + 65);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (stack.size() == 200){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
stack.push(t);
System.out.println(name + " 压入 " + t);
this.notify();
}
}
}
package 第18个程序_多线程.a6_线程交互.练习3_生产者_消费者;
public class Consumer extends Thread{
MyStack stack = new MyStack();
String name;
public Consumer(){}
public Consumer(String name){
this.name = name;
}
public synchronized void run(){
char t;
while (true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (stack.size() == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t = stack.pop();
System.out.println(name + " 弹出 " + t);
this.notify();
}
}
}
package 第18个程序_多线程.a6_线程交互.练习3_生产者_消费者;
import java.util.ArrayList;
import java.util.List;
public class test {
public static void main(String[] args) {
List<Producer> producers = new ArrayList<>();
List<Consumer> consumers = new ArrayList<>();
MyStack stack = new MyStack();
for (int i = 0; i < 2; i++){
Producer producer = new Producer("producer " + (i+1));
producer.stack = stack;
producers.add(producer);
}
for(int i = 0; i < 5; i++){
Consumer consumer = new Consumer("consumer " + (i+1));
consumer.stack = stack;
consumers.add(consumer);
}
for (Producer producer:producers){
producer.start();
}
for (Consumer consumer:consumers){
consumer.start();
}
}
}
7 线程池
线程大量启动、结束 导致系统的性能变卡,响应变慢
引入线程池解决大量线程问题
线程池像生产者消费者模式,消费对象是运行的任务
https://blog.csdn.net/weixin_40598838/article/details/112861491
简单程序
自创线程池:
package 第18个程序_多线程.a7_线程池.s1_简单程序;
import java.util.LinkedList;
public class MyThread {
// 线程池大小
int size;
// 任务器
LinkedList<Runnable> list = new LinkedList<>();
//企图消费的线程
public MyThread(){
size = 10;
synchronized (list){
for (int i = 0; i < size; i++){
new ConsumerThread("任务消费者进程 " + (i+1)).start();
}
}
}
public void add(Runnable r){
synchronized (list){
list.add(r);
list.notify();
}
}
class ConsumerThread extends Thread{
public ConsumerThread(String name){
super(name);
}
Runnable r;
public void run(){
System.out.println("启动 " + this.getName());
while (true){
synchronized (list){
while (list.isEmpty()){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
r = list.removeLast();
list.notify();
}
System.out.println(this.getName() + " 获取到任务,开始执行");
r.run();
}
}
}
}
package 第18个程序_多线程.a7_线程池.s1_简单程序;
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 5; i++){
Runnable r = new Runnable() {
@Override
//空
public void run() { }
};
myThread.add(r);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

测试
任务执行时间都是 1 秒
隔 1 秒,向线程池中添加任务
然后间隔时间越来越短,执行任务的线程还没结束,新任务又来了
观察到线程池里 其他线程被唤醒来执行这些任务
package 第18个程序_多线程.a7_线程池.s2_测试线程池;
import java.util.LinkedList;
public class MyThread {
// 线程池大小
int size;
// 任务器
LinkedList<Runnable> list = new LinkedList<>();
//企图消费的线程
public MyThread(){
size = 10;
synchronized (list){
for (int i = 0; i < size; i++){
new ConsumerThread("任务消费者进程 " + (i+1)).start();
}
}
}
public void add(Runnable r){
synchronized (list){
list.add(r);
list.notify();
}
}
class ConsumerThread extends Thread{
public ConsumerThread(String name){
super(name);
}
Runnable r;
public void run(){
System.out.println("启动 " + this.getName());
while (true){
synchronized (list){
while (list.isEmpty()){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
r = list.removeLast();
list.notify();
}
System.out.println(this.getName() + " 获取到任务,开始执行");
r.run();
}
}
}
}
package 第18个程序_多线程.a7_线程池.s2_测试线程池;
public class test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
int sleep = 1000;
while (true){
myThread.add(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
try {
Thread.sleep(sleep);
sleep = sleep>100?(sleep-100):sleep;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

java自带线程池
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
第 1 个参数10:该线程池初始化了 10 个线程工作
第 2 个参数15:如果 10 个线程不够用,会自动增加到最多 15 个线程
第 3 个参数60:结合第 4 个参数 TimeUnit.SECONDS:经过 60 秒,多出的线程还没有接到活儿,就会回收,最后保持池子里就 10 个线程
第 5 个参数 new LinkedBlockingQueue():用来放任务的集合
execute方法用于添加新的任务
package 第18个程序_多线程.a7_线程池.s3_java自带线程池;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class test {
public static void main(String[] args) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("任务1");
}
});
}
}
练习
之前 查找文件内容 ,若文件特别多,就会创建很多线程。
改写:
用线程池来完成
初始化大小是 10 的线程池
遍历所有文件,当遍历到文件是 .java 时,创建一个查找文件的任务,把这个任务扔进线程池去执行,继续遍历下一个文件
package 第18个程序_多线程.a7_线程池.练习;
import java.io.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SearchFileThread {
private File file;
private String search;
public SearchFileThread(){}
public SearchFileThread(File file, String search){
this.file = file;
this.search = search;
}
private String readFile(File file){
try (
FileReader fileReader = new FileReader(file);
){
char[] dates = new char[(int) file.length()];
fileReader.read(dates);
return new String(dates);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public void Search(File file, String search){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,15,60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
File[] files = file.listFiles();
for (File file1: files){
if (file1.isDirectory()){
Search(file1,search);
}else if (file1.isFile() && file1.getName().endsWith(".java")){
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
char[] dates = new char[(int) file1.length()];
try (
BufferedReader bufferedReader = new BufferedReader(new FileReader(file1));
){
bufferedReader.read(dates);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (String.valueOf(dates).contains(search)){
System.out.println("找到字符串在 " + file1.getName() + " - " + file1.getAbsolutePath());
}
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
package 第18个程序_多线程.a7_线程池.练习;
import java.io.File;
public class test {
public static void main(String[] args) {
String path = "src\\第18个程序_多线程\\a7_线程池\\练习\\src";
File file = new File(path);
if(!file.exists()){
System.out.println("该文件夹不存在!");
return;
}
SearchFileThread searchFileThread = new SearchFileThread();
searchFileThread.Search(file,"File");
}
}

8 Lock 对象(同步)
和 synchronize 效果一样
Lock lock = new ReentrantLock(); 当前线程占用 lock 对象,其他线程就不能占用了
区别:
synchronized 块结束,会自动释放 someObject 的占用
lock 必须用 unlock 方法手动释放,unlock 放在 finally 中进行
synchronized 的方式
9 原子访问
volatile
分布式锁
https://github.com/apache/shardingsphere/issues/5882
禁止安全检查
management:
health:
db:
enabled: false
lock、synchronize 只在单 jvm 有效,多集群 会失效
3中实现方式:
数据库

redis:
redis 的key 唯一

CPU 告诉你硬盘和网络到底有多慢
https://cizixs.com/2017/01/03/how-slow-is-disk-and-network/
java多线程有什么作用 好处?
https://cloud.tencent.com/developer/article/1841361
作用:
1、java多线程能够将各个任务分开执行,分开后的任务会同步进行
无需等待更多时间,效率也会更高
比如下载文件时如果使用java多线程的话,就能够同时下载多个文件。
2、java多线程能够分段执行
假如一个线程出现网络卡死的情况,那么多线程不会一直等待网络恢复正常,而是先执行其他的访问,如果操作超时会自动报错并释放相应线程
https://pdai.tech/md/interview/x-interview.html#32-并发关键字
https://www.liaoxuefeng.com/wiki/1252599548343744/1306580710588449
synchronized:
https://blog.csdn.net/m0_53474063/article/details/112389756
Java 对象的内存布局
https://www.51cto.com/article/713308.html
volatile
https://blog.csdn.net/didi1663478999/article/details/98523122
https://blog.csdn.net/xueping_wu/article/details/124541419
Java线程池 Executor
public interface eService extends Executor {
}
xxx implements eService {
}
类{
class a implements Runnable{
@Override
public void run(){
...
}
}
eService service;
void cc(){
service.execute(); // 会执行 上面的run()方法
}
}






