欢迎光临
我们一直在努力

线程创建线程同步线程状态线程池整理

xuancheng阅读(4)

@[TOC](线程)
## 1.多线程基础
### 1.1进程与线程的概念
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(**进程是资源分配的最小单位**)

线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(**线程是cpu调度的最小单位**)

### 1.2 简单描述什么是并行,什么是并发?

并行:指两个或多个事件在同一时刻发生(同时发生)。
并发:指两个或多个事件在同一个时间段内发生。
通俗易懂版:
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不 支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。

### 1.3进程概念、线程概念、线程与进程联系
问题:请描述什么是进程,什么是线程,进程与线程之间的关系,并举例说明。

答:
进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
一个程序运行后至少有一个进程,一个进程中可以包含多个线程,但一个进程中至少包含一个线程。比如使用迅雷软件下载网络文件时,同时下载多个文件,就使用到了多线程下载。

## 2.创建线程的几种模式

– 1.继承Runnable接口(推荐使用这种方式)
– 2.继承Thread类
– 3.使用匿名内部类方式创建
– 4.Lambda方式创建线程
– 5.Callable接口

### 2.1 继承Thread类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

(2)创建Thread子类的实例,即创建了线程对象。

(3)调用线程对象的start()方法来启动该线程。
“`java
//使用继承Thread类方法

public class ThreadTest01 {
public static void main(String[] args) {
new MyThread().start();
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i <9 ; i++) {
System.out.println(“轩成笔记”);
}
}
}
“`
### 2.2 继承Runnable接口创建线程
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
“`java

public class MyRunnableTest01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
//主线程
for (int i = 0; i <100 ; i++) {
System.out.println(“旺旺旺”+i);
}

}
}

class MyRunnable implements Runnable{

@Override
public void run() {
for (int i = 0; i <100 ; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}

“`

### 2.3 使用匿名内部类创建线程
和Runnable区别不大,只是把它匿名了
“`java
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(“轩成笔记”);
}
}).start();
}
}

“`
### 2.4使用Lambda创建线程
Lambda格式(参数类型,参数)->{代码}
省略方法

– 1.小括号内参数的类型可以省略; .
– 2.如果小括号内有且仅有一个参,则小括号可以省略;
– 3.如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号

“`java
//使用Lambda方法

public class ThreadTest06 {
public static void main(String[] args) {
new Thread(()->{
for (int i = 0; i <9 ; i++) {
System.out.println(i);
}
}).start();
}
}
“`
### 2.5使用Callable接口创建线程
和runable接口差不多
“`java
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test {
public static void main(String[] args) throws Exception {
FutureTask<String> sf = new FutureTask<>(new MyThread08());
new Thread(sf).start();
System.out.println(“获取线程返回值为+”+sf.get());

}
}

class MyThread08 implements Callable<String>{
@Override
public String call() throws Exception {
return “轩成笔记”;
}
}
“`
### 2.6 Thread和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

## 3.线程同步
### 3.1同步代码块
同步代码块:
synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:synchroni zed(同步锁){需要同步操作的代码}

“`java
private static void method1() {
//创建线程对象
Ticket t = new Ticket();
//创建三个窗口
Thread t1 = new Thread(t,”窗口1″);
Thread t2 = new Thread(t,”窗口2″);
Thread t3 = new Thread(t,”窗口3″);
//同时卖票
t1.start();
t2.start();
t3.start();
}
import jdk.nashorn.internal.ir.CallNode;

public class Ticket implements Runnable {
private int ticket=100;
Object lock = new Object( );
//卖票操作
@Override
public void run() {
//开启买票窗口
while (true){
//同步代码块:小括号中要求写入多个线程共享的对象一 对象锁/同步锁
synchronized (lock){//this也可以
if (ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取当前线程的名称
String name = Thread.currentThread().getName();
System.out.println(name+” 正在卖 “+ticket–);
}else {
return;
}}
}
}
}

“`
### 3.2 同步方法

“`java
private static void method2() {
//创建线程对象
Ticket01 t = new Ticket01();
//创建三个窗口
Thread t1 = new Thread(t,”窗口1″);
Thread t2 = new Thread(t,”窗口2″);
Thread t3 = new Thread(t,”窗口3″);
//同时卖票
t1.start();
t2.start();
t3.start();
}
public class Ticket01 implements Runnable {
private int ticket=100;
Object lock = new Object( );
//卖票操作
@Override
public void run() {
//开启买票窗口
while (true){
sellTicket();
}
}

private synchronized void sellTicket() {
//同步代码块:小括号中要求写入多个线程共享的对象一 对象锁/同步锁

if (ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取当前线程的名称
String name = Thread.currentThread().getName();
System.out.println(name+” 正在卖 “+ticket–);
}
}
}

“`
### 3.3 lock锁
| java.util. concurrent. locks .Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操
同步代码块/同步方法具有的功能L ock都有,除此之外更强大更体现面向对象。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:

public void lock() 加同步锁。
public void unlock() )释放同步锁。

“`java
private static void method3() {
//创建线程对象
Ticket02 t = new Ticket02();
//创建三个窗口
Thread t1 = new Thread(t,”窗口1″);
Thread t2 = new Thread(t,”窗口2″);
Thread t3 = new Thread(t,”窗口3″);
//同时卖票
t1.start();
t2.start();
t3.start();
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket02 implements Runnable {
private int ticket=100;
Lock lock= new ReentrantLock();
//卖票操作
@Override
public void run() {
//开启买票窗口
while (true){
lock.lock();
//同步代码块:小括号中要求写入多个线程共享的对象一 对象锁/同步锁

if (ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取当前线程的名称
String name = Thread.currentThread().getName();
System.out.println(name+” 正在卖 “+ticket–);
}lock.unlock();
}}

}

“`
## 4.线程状态/线程调度
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
– 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
– 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
– 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
![线程状态图](https://img-blog.csdnimg.cn/20200803171012829.bmp?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDg4Mzk3MQ==,size_16,color_FFFFFF,t_70)
### 4.1 Waiting和Inotify
Wating状态在API中介绍为:一个 正在无限期等待另一个线 程执行一个特别的 (唤醒)动作的线程处于这一-状态
1)wait Ihotify方法,它是object 中的方法,建bject对象调用(箬试题:以下哪些方法是/不是0bject/ Thread中的方法)
2)wait和Inotify方法必须由同一个 锁对象调用
3)wait和Inotify方法必须在同步代码块或者同步函数中使用

“`java
/*Wating状态在API中介绍为:一个 正在无限期等待另一个线 程执行一个特别的 (唤醒)动作的线程处于这一-状态
1)wait Ihotify方法,它是object 中的方法,建bject对象调用(箬试题:以下哪些方法是/不是0bject/ Thread中的方法)
2)wait和Inotify方法必须由同一个 锁对象调用
3)wait和Inotify方法必须在同步代码块或者同步函数中使用
*/
public class Test01 {
private static Object obj = new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (true){
synchronized (obj){
try {
System.out.println(Thread.currentThread().getName()+”获取到锁对象,调用wait方 法进入Waiting状态,释放锁对象”);
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”从Waiting状态醒来,获取到对象锁,继续执行了”);

}
}

}
},”等待线程”).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
synchronized (obj){
try {
System.out.println(Thread.currentThread().getName()+”等待3秒钟”);
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println(Thread.currentThread().getName()+”获取到锁对象,调用notify方法,释放锁对象”);
obj.notifyAll();
}
}
}

}
},”唤醒线程”).start();
}
}

“`
### 4.2线程优先级
static int MAX_PRIORITY
线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
分配给线程的默认优先级,取值为5。
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。

“`java
/*线程中常用的方法2:一线程的优先级(1-10),优先级高的抢占CPU时间片的时间相对多一些
最高:10最低:1默认:5
public static final int MAX_PRIORITY线 程可以拥有的最大优先级。
public static final int MIN_ PRIORITY线 程可以拥有的最小优先级。
public static final int NORM_ PRIORITY分配给线程的默认优先级。
public final int getPriority() 返回此线程的优先级。
public final void setPriority( int newPriority). 更改此线程的优先级。
*/
public class ThreadTest02 {
public static void main(String[] args) {
System.out.println(Thread.MAX_PRIORITY);
System.out.println(Thread.MIN_PRIORITY);
System.out.println(Thread.NORM_PRIORITY);
Thread t = new Thread(new MyThreadTest02());
t.setName(“t”);
Thread t1 = new Thread(new MyThreadTest02());
t1.setName(“t1”);
System.out.println(t.getPriority());
System.out.println(t1.getPriority());
t.setPriority(10);
t1.setPriority(1);
t.start();
t1.start();

}
}
class MyThreadTest02 extends Thread{
@Override
public void run() {
for (int i = 0; i <50 ; i++) {
System.out.println(Thread.currentThread());
}
}
}

“`
### 4.3线程方法
4.3.1 线程中常用的方法1:一**获取线程对象,取名,赋名**

“`java
/*线程中常用的方法1:一获取线程对象,取名,赋名
public static Thread cbirrentThread() 返回对当前正在执行的线程对象的引用。
public final String getName() 返回此线程的名称。
public final void setName(String name) 将此线程的名称更改为等于参数name。
*/
public class ThreadTest03 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread01());
t1.start();//Thread-1
Thread t2 = new Thread(new MyThread01());
t2.setName(“t2”);
t2.start();//t2

}
}
class MyThread01 extends Thread{
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println(t.getName());
}
}
“`
**4.3.2 interrupt中断方法**

“`java
package demo01;
/*睡眠太久,中途醒来的方法:
public void interrupt() 中断这个线程。
*/
public class ThreadTest04 {
public static void main(String[] args) {
Thread t = new Thread(new MyThreadTest04());
t.setName(“t”);
t.start();
//让它2秒之后结束
try {
Thread.sleep(1000*2);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();

}
}
class MyThreadTest04 implements Runnable{

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+”开始”);
try {
Thread.sleep(1000*60*60*24);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”结束”);
}
}

“`
**4.3.3线程中常用的方法5:**
怎么合理的终止一 个线程?不建议使用API中的Stop()方法
终止线程的方式
“`java
/*线程中常用的方法5:
怎么合理的终止一 个线程?不建议使用API中的Stop()方法
终止线程的方式
*/
public class ThreadTest05 {
public static void main(String[] args) throws InterruptedException {
MyThreadTest05 mt = new MyThreadTest05();
Thread t= new Thread(mt);
t.setName(“t”);
t.start();
//5秒钟之后终止线程
Thread.sleep(1000*5);
//终止线程
mt.run=false;

}
}
class MyThreadTest05 implements Runnable{
boolean run=true;

@Override
public void run() {
for (int i = 0; i <50 ; i++) {
if (run){
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
return;
}
}
}
}

“`
## 5.线程池
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,
无需反复创建线程而消耗过多资源。

**使用线程池的好处**
1.降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3.提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内
存,而把服务器累趴下(每个线程需要大约1 MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

**使用线程池的步骤**
1.创建线程池对象。
2.创建Runnable接口子类对象。(task)
3.提交Runnable接口子类对象。(take task)
4.关闭线程池(- -般不做)。

“`java
public class MyRunnable implements Runnable {
@Override public void run() {
System.out.println(“我要一个教练”);
try {Thread.sleep(2000); }
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“教练来了: ” + Thread.currentThread().getName());
System.out.println(“教我游泳,交完后,教练回到了游泳池”); } }

public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors . newF ixedThreadPool (2);/ /包含2个线程对象
//创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
// Thread t = new Thread(r);
// t.start(); —>调用MyRunnab1e中的run()
//从线程池中获取线程对象,然后调用My Runnabl e中的run()
service. submit(r);
//再获取个线程对象,调用lyRunnable中的run()
service. submit(r);
service. submit(r);
//注意: submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
//将使用完的线程又归还到了线程池中
//关闭线程池
//service. shutdown();
}
}

“`

世界,您好!

xuancheng阅读(68)

欢迎使用WordPress。这是您的第一篇文章。编辑或删除它,然后开始写作吧!

大前端WP主题 更专业 更方便

联系我们联系我们