Java多线程知识点
线程与进程
多线程技术概述
进程与线程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。
线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。
县城实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径有空也i划分成若干个线程。
同步与异步
同步:排队执行,效率低但是安全
异步:同时执行,效率高但是数据不安全
并发与并行
并发:指两个或多个时间在同一时间段内发生
并行:同时执行
继承Thread
Ctrl+P:查看方法参数
Ctrl+Q:查看类、方法、属性注释
每个线程都拥有自己的栈空间,共用一份堆内存
由一个线程执行的方法,
public class day528 {
public static void main(String[] args) {
MyThread my = new MyThread();
my.start();
for (int i = 0; i < 10; i++) {
System.out.println("开启的线程"+i);
}
}
}
public class MyThread extends Thread{//继承Thread类
/**
* run方法就是线程要执行的任务方法
*/
@Override
public void run() {
//这里的代码,就是一条新的执行路径
//这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start()来启动任务
// 调用父类 super.run();
for (int i = 0; i < 10; i++) {
System.out.println("主线程main"+i);
}
}
}
输出结果:
主线程main0
开启的线程0
主线程main1
开启的线程1
主线程main2
开启的线程2
主线程main3
开启的线程3
主线程main4
开启的线程4
主线程main5
开启的线程5
主线程main6
开启的线程6
主线程main7
开启的线程7
主线程main8
开启的线程8
主线程main9
开启的线程9
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oANlGOCW-1623171979737)(C:\Users\Sang\AppData\Roaming\Typora\typora-user-images\image-20210528141557386.png)]
继承Thread 的匿名内部类
public class Demo2 {
public static void main(String[] args) {
new Thread(){ //匿名内部类,创建一个类,没定义(名称),但指定父Thread
@Override
public void run() {//重写run方法
for (int i = 0; i < 10; i++) {
System.out.println("匿名内部类" + i);
}
}
}.start();//并且 创建了对象 调用一次start()方法 通过匿名内部类 实现了一个线程 还是继承Thread
for (int i = 0; i < 10; i++) {
System.out.println("main方法"+i);
}
}
}
Thread类(不管是通过继承Thread或者实现Runnable 都需要使用Thread类)
实现Runnable
public class Demo3 {
public static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("用于给线程执行的任务"+i);
}
}
}
/**
* //实现Runable 与 继承Thread相比有如下优势:
* 1.通过创建任务的方式,然后给线程分配的方式,来实现的多线程
* 更适合多个线程同时执行相同任务的情况
* 2.避免继承所带来的局限性 Java 单继承 多实现
* 3.任务与线程本身是分离的,提高了程序的健壮性。
* 4.后续学习的线程池技术,接受Runnable类型的人五,不接受Thread类型的线程
*/
public static class Demo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("main方法"+i);
}
}
}
}
设置和获取线程名称
currentThread //获取当前正在执行的线程对象(Thread)
getName() //获取线程名称
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
new Thread(new MyRunable()).start();
}
static class MyRunable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
线程休眠Sleep
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
System.out.println(i);
Thread.sleep(1000); //long millis 毫秒 int nanos 纳秒
}
}
输出结果:每隔1000ms 打印0-9 .
线程阻塞
常见的文件读取,接收用户输入 //耗时操作
线程中断
一个线程是一个独立的执行路径,它是否应该结束,应该有其自身决定。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunable());
t1.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
Thread.sleep(1000);
}t1.interrupt();
}
static class MyRunable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("main进程已中断");
return; //加return 结束main和 run(),不加只中断main
}
}
}
}
输出结果:
main:0
Thread-0:0
Thread-0:1
main:1
Thread-0:2
main:2
main:3
Thread-0:3
Thread-0:4
main:4
Thread-0:5
main进程已中断
守护线程
线程: 分为 守护线程 和 用户线程
用户线程:当一个进程不包含任何的存活的用户线程时,进行结束。
守护线程:守护用户线程的。当最后一个用户线程结束时,所有守护线程自动死亡。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunable());
t1.setDaemon(true);
t1.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
t1.interrupt();
}
static class MyRunable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
//加return 结束main和 run(),不加只中断main
}
}
}
}
输出结果:
main:0
Thread-0:0
Thread-0:1
main:1
Thread-0:2
main:2
Thread-0:3
main:3
Thread-0:4
main:4
Thread-0:5
线程安全问题
线程安全1-同步代码块
格式: synchronized(锁对象){
}
public static void main(String[] args) throws InterruptedException {
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
private int count = 10;//10张票
private Object o = new Object();//将整个任务放进一个属性o里,即三个线程运行一个任务o,即一个锁
@Override
public void run() {
while (true) {
synchronized(o) {
if (count > 0) {
System.out.println("出票中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功! 余票:" + count);
}else {
break;
}
}
}
}
}
输出结果:
出票中
Thread-0出票成功! 余票:9
出票中
Thread-0出票成功! 余票:8
出票中
Thread-0出票成功! 余票:7
出票中
Thread-0出票成功! 余票:6
出票中
Thread-2出票成功! 余票:5
出票中
Thread-1出票成功! 余票:4
出票中
Thread-1出票成功! 余票:3
出票中
Thread-1出票成功! 余票:2
出票中
Thread-1出票成功! 余票:1
出票中
Thread-1出票成功! 余票:0
Process finished with exit code 0
线程安全2-同步方法
public static void main(String[] args) throws InterruptedException {
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
private int count = 10;//10张票
//private Object o = new Object();//将整个任务放进一个属性o里,即三个线程运行一个任务o,即一个锁
@Override
public void run() {
while (true) {
boolean flag = sale();
if (!flag){// 返回的是false
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
System.out.println("出票中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功! 余票:" + count);
}else{
return false;
}
return true;
}
}
输出结果:
出票中
Thread-0出票成功! 余票:9
出票中
Thread-0出票成功! 余票:8
出票中
Thread-0出票成功! 余票:7
出票中
Thread-0出票成功! 余票:6
出票中
Thread-0出票成功! 余票:5
出票中
Thread-0出票成功! 余票:4
出票中
Thread-0出票成功! 余票:3
出票中
Thread-0出票成功! 余票:2
出票中
Thread-0出票成功! 余票:1
出票中
Thread-0出票成功! 余票:0
公平锁与非公平锁
公平锁:线程排队执行
private Lock l = new ReentrantLock(true); // Lock 显示锁, fair 参数 为 true,就是公平锁
非公平锁:线程抢
public static void main(String[] args) throws InterruptedException {
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
private int count = 10;//10张票
//private Object o = new Object();
// 将整个任务放进一个属性o里,即三个线程运行一个任务o,即一个锁
private Lock l = new ReentrantLock(true);//fair参数为true,就是公平锁
@Override
public void run() {
while (true) {
l.lock();
boolean flag = sale();
if (!flag){// 返回的是false
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
System.out.println("出票中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功! 余票:" + count);
}else{
return false;
}
return true;
}
}
线程死锁
public static void main(String[] args) {
//线程死锁
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p
).start();
c.say(p);
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
public MyThread(Culprit c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run() {
p.say(c);
}
}
//为了能在main方法里使用,加上静态static内部类修饰
static class Culprit{//罪犯
public synchronized void say(Police p){
System.out.println("罪犯:你放我,我放人质");
p.fun();
}
public synchronized void fun(){
System.out.println("警察放了罪犯,罪犯也放了人质");
}
}
static class Police{//警察
public synchronized void say(Culprit c){
System.out.println("警察:你放人质,我放了你");
c.fun();
}
public synchronized void fun(){
System.out.println("罪犯放了人质,警察放了罪犯");
}
}
输出结果:(锁了)
罪犯:你放我,我放人质
警察:你放人质,我放了你
多线程通信问题
生产者与消费者
public static void main(String[] args) {
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//多线程通讯问题,生产者与消费者问题
static class Cook extends Thread{//厨师线程
private Food f;
public Cook(Food f){
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
f.setNameAndTaste("油泼面","麻辣味");
}else {
f.setNameAndTaste("土豆粉","五香味");
}
}
}
}
static class Waiter extends Thread{//服务员线程
private Food f;
public Waiter(Food f){
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i%2==0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.setNameAndTaste("油泼面","麻辣味");
}else {
f.setNameAndTaste("土豆粉","五香味");
}
f.get();
}
}
}
static class Food{
private String name;
private String taste;
private boolean flag = true;
public synchronized void setNameAndTaste(String name,String taste) {
if(flag) {
this.name = name;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();//唤醒服务员
try {
this.wait();//厨师睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(!flag) { //false
System.out.println("服务员端走的菜的名称是:" + name + "口味是:" + taste);
flag = true;
this.notifyAll();//唤醒厨师
try {
this.wait();//服务员睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果:
服务员端走的菜的名称是:油泼面口味是:麻辣味 x50
服务员端走的菜的名称是:土豆粉口味是:五香味 x50
线程的六种状态
new: 表示线程刚被创建,没有启动的线程状态
Runnable: 在Java虚拟机中执行的线程状态
Blocked: 阻塞状态(线程排队执行时的状态)
Waiting: 无限期等待另一个线程执行特定操作的状态(线程休眠)
TimeWaiting:即时等待的状态(也可以被唤醒)
Trminated: 线程死亡,已退出的线程状态
带返回值的线程Callable
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
return 100;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> c = new MyCallable();
FutureTask<Integer> f = new FutureTask<>(c);
new Thread(f).start();
Integer Int = f.get();//获取返回值时,会导致线程停止,直到获取返回值
System.out.println("返回值为:"+Int);
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
输出结果:
0
1
2
3
4
5
6
7
8
9
返回值为:100
0
1
2
3
4
5
6
7
8
9
线程池 Executors
概述:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程
就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容
器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
Java中的四种线程池 .
无论是缓存线程池、定长线程池、单线程线程池,最终获取类型 都是ExecutorService
ExecutorService
1. 缓存线程池
public static void main(String[] args) {
//newCachedThreadPool(); 创建缓存的线程的池
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {//线程重复使用,即使用缓存池里空闲的线程
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
}
输出结果:
pool-1-thread-1不积硅步
pool-1-thread-2不积硅步
pool-1-thread-3不积硅步
pool-1-thread-3不积硅步 // 使用的是缓存池里的线程,而不是线程4
2.定长线程池
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
}
}
输出结果:
pool-1-thread-2不积硅步
pool-1-thread-1不积硅步
pool-1-thread-1不积硅步
3.单线程线程池
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"不积硅步");
}
});
}
输出结果:
pool-1-thread-1不积硅步
pool-1-thread-1不积硅步
pool-1-thread-1不积硅步
4.周期定长线程池:
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/* *//*
定时执行一次
参数1:定时执行的任务
参数2:时长数字
参数3:时长数字的时间单位TimeUnit的常量指定
*//*
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"无以至千里");
}
},5, TimeUnit.SECONDS);*/
/*
周期性执行任务 根据某个周期固定执行参数
参数1:任务
参数2:延迟时长数字(第一次执行在什么时间以后)
参数3:周期时长数字 (每间隔多久执行一次)
参数4:时长数字的单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("2秒过去了");
}
},5,2,TimeUnit.SECONDS);
}
输出结果:
2秒过去了 //每间隔2s ,输出“2秒过去了”
2秒过去了
2秒过去了
2秒过去了
2秒过去了
2秒过去了
Lambad表达式
函数式编程思想
在代码上即把匿名内部类的 类的部分 删掉,保留一个抽象方法参数部分和方法体
范例1:
/*
Lambda表达式
函数式编程思想
面向对象: 创建对象调用方法,解决问题
*/
public static void main(String[] args) {
//冗余的Runnable代码
/*Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("无以至千里");
}
});
t.start();*/
输出结果:
Thread-0无以至千里
Lambda 表达式写法
Thread t = new Thread(()-> {
System.out.println(Thread.currentThread().getName()+"无以至千里");
});
t.start();
}
输出结果:
Thread-0无以至千里
范例2:
public static void print(MyMath m,int x,int y){
int num = m.sum(x,y);
System.out.println(num);
}
static interface MyMath{
int sum(int x,int y);
}
public static void main(String[] args) {
print(new MyMath() {
@Override
public int sum(int x, int y) {
return x+y;
}
},100,200);
}
输出结果:
300
Lambda 表达式写法
public static void print(MyMath m,int x,int y){
int num = m.sum(x,y);
System.out.println(num);
}
static interface MyMath{
int sum(int x,int y);
}
public static void main(String[] args) {
print((int x, int y) ->{
return x+y;
},100,200);
}
输出结果:
300