进程与线程
Process与Thread
- 说起进程就不得不说下程序。程序是指指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念。
- 而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
- 通常在一个进程中可以包含多个线程,当然一个进程至少有一个线程,不然没有存在的意义。显示CPU调度和执行的单位
注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的快,所以就有同时执行的错觉
线程的创建
- Thread、Runnable、Callable
- 继承Thread
package com.xy.Test;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
// 练习Thread、实现多线程
public class Test02 extends Thread {
private String url;
private String name;
public Test02(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownload webDownload = new WebDownload();
webDownload.downloader(url, name);
System.out.println("下载的图片名字为: " + name);
}
public static void main(String[] args) {
Test02 t2 = new Test02("<url>", "apache1");
Test02 t3 = new Test02("<url>", "apache2");
t1.start();
t2.start();
t3.start();
}
}
// 下载类
class WebDownload {
// 下载方法
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 实现Runnable接口 ```java package com.xy.Test;
// 创建方法二: 实现Runnable接口,重写run方法,执行线程需要丢入runnable接口实现类 // 调用start方法 public class Test03 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("run线程" + i);
}
}
public static void main(String[] args) {
// 创建runnable接口的实现类对象
Test03 t1 = new Test03();
// 创建线程对象,通过线程对象来开启我们的线程代理
Thread thread = new Thread(t1);
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i);
}
}
}
```java
package com.xy.Test;
// 多个线程同时操作同一个对象
// 买火车票的例子
// 多个线程操作同一个资源的情况下,线程不安全,数据混乱
public class Test04 implements Runnable{
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
try {
Thread.sleep(200);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第几张:" + ticketNums-- + "票");
}
}
public static void main(String[] args) {
Test04 test04 = new Test04();
new Thread(test04, "小明").start();
new Thread(test04, "老师").start();
new Thread(test04, "黄牛").start();
}
}
package com.xy.Test;
// 模拟龟兔赛跑
public class Race implements Runnable {
// 胜利者
public static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
// 模拟兔子休息
if ((Thread.currentThread().getName().equals("兔")) && (i == 20)) {
try {
Thread.sleep(1);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断比赛是否结束
boolean flag = gameOver(i);
if(flag) {
break;
}else {
}
System.out.println(Thread.currentThread().getName()+ "--> 跑了" + i +"步");
}
}
// 判断是否完成比赛
private boolean gameOver(int steps) {
// 判断胜利者是否存在
if(winner != null) { // 已经存在胜利者
return true;
} {
if(steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is" + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race, "兔").start();
new Thread(race, "龟").start();
}
}
- 实现Callable接口 ```java package com.xy.Test;
import com.sun.org.apache.xpath.internal.operations.Bool; import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.*;
// 练习Thread、实现多线程
public class TestCallable implements Callable
private String url;
private String name;
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
WebDownload1 webDownload1 = new WebDownload1();
webDownload1.downloader(url, name);
System.out.println("下载的图片名字为: " + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://images.unsplash.com/photo-1650844565749-1f10c8aaa9f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=764&q=80", "apache");
TestCallable t2 = new TestCallable("https://images.unsplash.com/photo-1650844565749-1f10c8aaa9f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=764&q=80", "apache1");
TestCallable t3 = new TestCallable("https://images.unsplash.com/photo-1650844565749-1f10c8aaa9f9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=764&q=80", "apache2");
// 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
// 获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
// 关闭服务
ser.shutdownNow();
}
}
// 下载类 class WebDownload1 { // 下载方法 public void downloader(String url, String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); } catch (IOException e) { e.printStackTrace(); } } }
<a name="R3Wck"></a>
## 代理
<a name="hZZjy"></a>
### 静态代理
```java
package com.xy.proxy;
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
}
}
interface Marry {
void HappyMarry();
}
// 真实角色
class You implements Marry {
@Override
public void HappyMarry() {
System.out.println("你结婚啦");
}
}
// 代理角色
class WeddingCompany implements Marry {
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void before() {
System.out.println("结婚之前布置现场");
}
private void after() {
System.out.println("收尾款");
}
}
Lamda表达式
- 避免匿名内部类定义过多
其实质属于函数式编程的概念
new Thread(() -> System.out.println("多线程学习")).start();
理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在
- 函数式接口的定义:
- 任何接口,如果包含唯一一个抽象方法,那么它就是一个函数式接口
- 对于函数式接口,我们可以通过lambda表达式来闯将接口的对象 ```java package com.xy.Lambda;
/**
- 推导lambda表达式 */
// 定义一个函数式接口(一个接口,只包含一个抽象方法) interface ILike { void lambda(); }
public class TestLambda { private static ILike like; public static void main(String[] args) { like = () -> { System.out.println(“lambda表达式”); }; like.lambda(); } }
<a name="rRbfR"></a>
## 线程状态
![](https://cdn.nlark.com/yuque/0/2022/jpeg/13002623/1651295009027-3001f53c-ed1c-46c4-b662-c2cea8966cec.jpeg)
| 方法 | 说明 |
| --- | --- |
| setPriority(int newPriority) | 更改线程的优先级 |
| static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
| void join() | 等待该线程终止 |
| static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
| void interrupt() | 中断线程,别用这个方式 |
| boolean isAlive() | 测试线程是否处于活动状态 |
```java
package com.xy.State;
public class Sleep2 {
static public void tenDown() {
int num = 10;
while (true){
if (num <= 0) break;
try {
Thread.sleep(1000);
System.out.println(num--);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
tenDown();
}
}
package com.xy.State;
// 测试join方法 、、 插队
public class Join implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程VIP来了" + i);
}
}
public static void main(String[] args) {
// 启动线程
Join join = new Join();
Thread thread = new Thread(join);
thread.start();
for (int i = 0; i < 1000; i++) {
if(i == 200) {
try {
thread.join(); // 插队
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main" + i);
}
}
}
package com.xy.State;
// 测试stop
// 1.建议线程正常停止 -------> 利用次数,不建议死循环
// 2.设置一个标志位 -----> 设置一个标志位
// 3.不要使用stop或者destroy等过时或jdk不建议使用的方法
public class Stop implements Runnable {
// 1. 设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run....Thread" + i++ );
}
}
//2. 设置一个公开的方法停止线程,转换标志位
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
Stop stop = new Stop();
new Thread(stop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if (i == 900) {
stop.stop();
System.out.println("线程停止");
}
}
}
}
package com.xy.State;
// 线程礼让
public class Yield{
public static void main(String[] args) {
A1 a1 = new A1();
new Thread(a1, "a").start();
new Thread(a1, "b").start();
}
}
class A1 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行!");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "线程停止执行!");
}
}
线程状态
- NEW
- 尚未启动的线程处于此状态
- RUNNABLE
- 在Java虚拟机中执行的线程处于此状态
- BLOCKED
- 被阻塞等待监视器锁定的线程出于此状态
- WAITING
- 正在等待另一个线程执行特定动作的线程处于此状态
- TIMED_WAITING
- 正在等待另一个线程执特定行动作到达指定等待时间的线程, 处于此状态
- TERMINATED
- 已退出的线程出于此状态
一个线程可以在给定时间点处于一个状态。这些状态是不反应任何操作系统线程状态的虚拟机状态。
package com.xy.State;
// 观察测试线程的状态
public class State {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("//////////");
});
// 观察状态
Thread.State state = thread.getState();
System.out.println(state); // New
// 观察启动后
thread.start(); // 启动线程
state = thread.getState();
System.out.println(state); // Run
while (state != Thread.State.TERMINATED) {
// 只要线程不终止,就一直输出状态
state = thread.getState();
System.out.println(state);
}
}
}
线程优先级
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调用哪个线程来执行。
- 线程的优先级用数字表示,范围从1~10
- Thread.MIN_PROIORITY = 1;
- Thread.MAX_PROIORITY = 10;
- Thread.NORM_PROIORITY = 10;
- 使用以下方式改变或获取优先级
- getPriority .setPriority(int XXX) ```java package com.xy.Priority;
public class Priority implements Runnable { @Override public void run(){ System.out.println(Thread.currentThread().getName() + “———->” + Thread.currentThread().getPriority() ); } public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + “———->” + Thread.currentThread().getPriority() ); Priority priority = new Priority(); Thread t1 = new Thread(priority, “t1”); Thread t2 = new Thread(priority, “t2”); Thread t3 = new Thread(priority, “t3”); Thread t4 = new Thread(priority, “t4”); Thread t5 = new Thread(priority); Thread t6 = new Thread(priority);
// 设置优先级再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
}
}
<a name="pYllE"></a>
### 守护(daemon)线程
- 线程分为**用户线程**和**守护线程**
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等待。。。
```java
package com.xy.Priority;
// 测试守护线程
public class Daemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true); // 默认为false表示用户线程,正常的线程都是用户线程
thread.start(); // 上帝启动
new Thread(you).start();
}
}
class God implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("上帝保佑你");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一生都开心的活着");
}
System.out.println("====goodbye!====");
}
}
线程同步
- 并发: 同一个对象被多个线程同时操作
package com.xy;
// 线程不安全,有负数
public class UnSafe {
public static void main(String[] args) {
Buy buy = new Buy();
new Thread(buy, "w").start();
new Thread(buy, "n").start();
new Thread(buy, "h").start();
}
}
class Buy implements Runnable {
private int tickNums = 15;
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buy() throws InterruptedException {
if(tickNums <= 0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()
+ "拿到了" + tickNums--
);
}
}
死锁
- 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同事拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。