多线程Thread
多线程
三种创建方法
- 获取当前线程的名字
Thread.currentThread().getName();
new Thread(tr,a);
当有多个线程的时候,可以给他赋予名字,a就是线程的名字
-
继承Thread类,继承thread类,重写run方法,编写执行体,new一个继承thread的类的对象,用.start()方法启动线程
假设一个class Tr extends继承了Thread类 Tr tr = new Tr(); tr.start(); //开启线程 -
实现Runnable接口(推荐使用),他是一个函数式接口,里面只有一个run方法
在Runnable中有两种状态Ready和Running,Ready就是准备好了等待CPU调度,Running就是真正的在运行,由CPU调度假设一个class Tr implements了Runnable接口 Tr tr = new Tr(); new Thread(tr).start(); //或者用lambda表达式 new Thread(()->{执行的代码块}).start() //开启线程 -
实现Callable接口(了解就好)
-
public class Tr implements Runnable{ @Override public void run(){ } public static void main(String[] args){ } }
-
其他知识点
- main方法就是main线程,他是主线程,也可以通过Thread.方法
- 线程调用run方法就是普通的调用方法,而线程调用start方法呢就是开启了多线程, 他是跟主线程交替执行的
- 同时执行,因为我们电脑只有一个CPU,线程的执行由cpu调度安排,所以线程可能不执行
- lambda表达式可以减少内部类的创建,提高代码效率
但是他仅限于函数式接口,就是只有一个方法的接口
线程的状态
新生,就绪,运行,阻塞,结束
new xxx runnable waiting destroy
线程停止
-
建议线程正常停止--->利用次数,不建议死循环。
-
建议使用标志位--->设置一个标志位 flag
在线程的run方法中使用 while(flag) -
推荐让线程自己停下来
-
不要使用stop或者destroy等过时或者JDK不建议使用的方法
还有方法上使用了@Deprecated注解的,不建议使用 -
package com.threadd; public class TestThread implements Runnable{ private boolean flag = true; @Override public void run() { // TODO Auto-generated method stub int i = 0; while(flag){ System.out.println("线程执行了"+i+++"次----"); } } public static void main(String[] args) { // TODO Auto-generated method stub TestThread tr = new TestThread(); new Thread(tr).start(); for(int i = 0;i<999;i++){ if(i==900){ tr.stop(); System.out.println("次线程停止了!"); } System.out.println("这是主线程执行的第"+i+"次////"); } } public void stop(){ this.flag = false; } }
sleep线程休眠
package com.threadd;
import java.text.SimpleDateFormat;
import java.util.*;
public class TestThread02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// try {
// tenDown();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//打印当前系统时间
Date startTime = new Date (System.currentTimeMillis());//获取系统当前时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date (System.currentTimeMillis());//更新系统当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 模拟倒计时
public static void tenDown() throws InterruptedException {
int num = 10;
while (true) {
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0) {
break;
}
}
}
}
- sleep可以模拟网络延时,倒计时等
每个对象都有一把锁,sleep不会释放锁
线程礼让yield
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 让线程从运行状态转为就绪状态
- 礼让可能成功也可能不成功,还是要看cpu的心情
插队vip线程join
- 通过thread.join()插队,会让其他线程阻塞
- 等vip线程跑完了,其他线程才能执行,少用
观察线程状态
Thread.getState();
Thread.state对象接收
线程的优先级Priority
Priority [praɪˈɒrəti]
- 获取优先级
thread.getPriority(); - 设置优先级 优先级范围是1~10
thread.setPriority(int arg); - 参数越大,优先级越高,不过也要看cpu的调度,也有可能不按优先级运行
- arg可以是
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5 - main线程默认优先级是5,但是一般是先启动main线程
不然无法创造其他线程 - 如果一个线程高的等待一个线程低的就会导致一个
优先级倒置的问题,从而引起性能问题
守护线程 daemon
daemon [ˈdiːmən]
Thread.setDaemon(true);
默认是false表示用户线程,正常线程都是用户线程
守护线程可以为死循环,他在其他线程执行完后会自动结束
线程同步
队列和锁
锁机制:synchronized
线程同步必然会引起一个性能降低
要保证安全就会损失性能,要保留性能就会导致不安全
鱼和熊掌不可兼得
线程安全问题
因为他们的内存都是相互不影响的,可能会同时运行
线程不安全的例子:
- 几个人同时抢票
- 两个人同时取钱
- 线程不安全的集合
锁机制 synchronized
synchronize是如何保证线程安全的? 以及他的原理是什么
就当我们给 一个资源加上 synchronize 进行修饰的时候, 这个时候有一个线程要使用他,他就会拿到当前资源的一个锁对象,
这个时候另外一个线程也想用它,此时synchronize就会对其他线程进行阻塞,一直到当前线程将锁释放了,另个线程才能拿到该资源的锁对象. 这样就保证了我的一个安全性
每个对象都有一把锁
关键字 synchronized (同步的) 也属于修饰符,加上关键字的方法就成了同步方法
public synchronized void method(){} 这就是个同步方法
synchronized是隐式锁,他有作用域和自动释放
synchronized有代码块锁和方法锁
同步方法:可以理解成就算有多个线程调用这个方法,也是需要排队,一个个的调用
同步块:可以锁任何对象,记住!锁的一般都是公共资源需要修改的对象
synchronized (obj){ }
obj称之为同步监视器,这个对象一般就是需要被增删改查的一个对象
在使用一个集合添加数据的时候,可能会同时插入到一个位置,导致覆盖了之后最后的长度错误
有一个 juc安全类型的集合可以解决 CopyOnWriterArrayList ,或者使用同步块儿来解决
死锁 deadLock
如何保证资源只有一份:用static修饰对象即可
死锁如何理解:
就是多个线程互相拿着对方需要的资源,形成僵局,导致程序卡死,也就是线程太贪
就比如两个人A和B,然后有两个静态资源手机和电脑
上一秒A想玩手机B想玩电脑,下一秒A又想玩电脑B又想玩手机
如果此时A一个人抱着两个东西的时候,是没办法互换的,所以一个人只能选择拿手机还是拿电脑,不能同时霸占两个东西,这样就会导致死锁的出现
案例:
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
Lock锁
java.util.concurrent.locks包下提供的一个Lock接口
其实他的作用等同于 synchronized ,
只不过他是通过显式定义同步锁对象来实现同步
lock是显式锁,需要手动开启和关闭锁
synchronized是隐式锁,他有作用域和自动释放
Lock锁性能更好
优先使用顺序:
Lock > 同步代码块(已经进入了方法体,分配了相应的资源) > 同步方法(在)方法体之外
ReentrantLock 可重入锁
ReentrantLock类实现了Lock
定义lock锁
private final ReentrantLock lock = new ReentrantLock();
一般使用try finally
在try代码块中开头加上 lock.lock(); 开启锁
在finally中 使用 lock.unlock(); 关闭锁
线程协作
线程通信
因为所有对象都有一把锁,然后Object类提供了两个方法
| 方法名 | 作用 |
|---|---|
| wait() | 表示线程一直等待,知道其他线程通知,与sleep不同,他会释放锁 |
| wait(long timeout) | 指定等待的毫秒数 |
| notify() | 唤醒一个处于等待状态的线程 |
| notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度 |
他们都只能在同步方法或者同步代码块儿中使用,不然会抛出异常
协作模型:生产者、消费者模式
(管程法|买炸鸡的例子),(信号灯法flag 观众看电视,演员录制节目的例子)
生产者,消费者,缓冲区
线程池
- corePoolSize:核心池大小
- maxmumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间会终止
Executorservice就是线程池的真正接口
Executor是一个工具类,也是线程池的工厂类,
//1.创建服务,创建线程池
//newFixedThreadPool参数为:线程池大小
Executorservice service = Executors.newFixedThreadPool (int 存储的线程数量);
//执行线程
service.execute(线程);
//关闭线程
service.shutdown();
JUC?
JUC就是java.util工具包下的三个concurrent包
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
本文来自博客园,作者:没有烦恼的猫猫,转载请注明原文链接:https://www.cnblogs.com/maomao777/p/16057470.html

浙公网安备 33010602011771号