多线程
main()是主线程
线程创建
三种创建方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
继承Thread类
//线程创建方式一 :
/*
第一步:继承Thread类
第二步:重写run()方法
第三步:创建继承了Thread类的对象 , 调用start()方法启动。
*/
public class TestThread1 extends Thread{
//自定义run方法的线程
@Override
public void run() {
//线程执行体
for (int i = 0; i < 200; i++) {
System.out.println("有人在写笔记-->"+i);
}
}
//主线程
public static void main(String[] args) {
//创建线程对象
TestThread1 testThread1 = new TestThread1();
//start() 方法用来 启动线程;
testThread1.start();
for (int i = 0; i < 3000; i++) {
System.out.println("有人在在睡觉sleep-->"+i);
}
}
}
主线程走star 子线程走run
public class TestThread1 extends Thread{//(第二部)实现一个线程类 通过继承Thread
private String url;
private String name;//用构造器放入图片地址和名字,同时定义了调用线程类的初始属性,这是构造器的功能,没有具体的值,类似类的形参
public TestThread1(String url,String name){
this.url = url;
this.name = name;
}
//run方法为线程的执行体,写线程类时首先重写run方法 是每一个线程都要做的事情,每一个线程抽象出来的具体做法写在线程类中,然后通过主方法启动线程,最后线程会同时启动
@Override
public void run() {
WebDownLoade webDownLoade = new WebDownLoade();
webDownLoade.downloder(url,name);//下载图片通过类名调用方法
System.out.println("下载了文件名"+name);//输出一下名字
}
//第三步在主方法上输入地址及启动线程
public static void main(String[] args){
//创建线程对象,也就是放入实参的过程,需要new线程类对象实现
TestThread1 t1 =new TestThread1("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2771978851,2906984932&fm=26&gp=0.jpg","2.jpg");
TestThread1 t2=new TestThread1("https://tse4-mm.cn.bing.net/th/id/OIP.ZTmNathhKflijArYDaQEDAHaEo?w=234&h=180&c=7&o=5&dpr=1.25&pid=1.7","3.jpg");
TestThread1 t3 =new TestThread1("https://tse3-mm.cn.bing.net/th/id/OIP.H986GDFYxiuR5pDYvu5uvgHaLH?w=115&h=180&c=7&o=5&dpr=1.25&pid=1.7","4.jpg");
TestThread1 t4 =new TestThread1("https://tse1-mm.cn.bing.net/th/id/OIP.KYaksEiOCQPXilet3OQ1HQHaEo?w=266&h=180&c=7&o=5&dpr=1.25&pid=1.7","5.jpg");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//下载器(第一步)
class WebDownLoade{
// 下载方法 (通过把图片转文件下载)
public void downloder(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloder方法出现问题");
}
}
}
实现runnable接口
public class TextThread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("kandaima");
}
}
public static void main(String[] args) {
//runnable接口实现类对象
TextThread2 thread2 = new TextThread2();
// 创建线程对象,通过线程对象开启线程, 代理
// Thread thread = new Thread(thread2);
// thread.start();
// 等于下面一行
new Thread(thread2).start();
for (int i = 0; i < 200; i++) {
System.out.println("多线程");
}
}
}
改造thread类为Runnable接口
public class TestThread1 implements Runnable
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
new Thread(t4).start();
并发问题
//多个线程操作同一个对象
public class TestTheard3 implements Runnable{
// 票数
private int ticketNums = 10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
//设置延时200ms
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestTheard3 ticked = new TestTheard3();
new Thread(ticked,"小明").start();
new Thread(ticked,"小黑").start();
new Thread(ticked,"小白").start();
new Thread(ticked,"小王").start();
}
}
小王拿到了第10票
小白拿到了第9票
小黑拿到了第10票
小明拿到了第10票
小明拿到了第6票
小白拿到了第7票
小黑拿到了第8票
小王拿到了第5票
小王拿到了第4票
小白拿到了第3票
小黑拿到了第2票
小明拿到了第1票
Process finished with exit code 0
当多个线程操作同一个对象时,拿到同一个数据,线程一起工作。
语法区别,继承Thread类要启动线程,需要new 这个对象然后。start
实现Runnable接口启动线程,需要newThread,然后丢入new 的Runnable类的对象
代理模式
真实对象和代理对象都要实现同一个接口
代理模式更像一个工具类,代理对像总结出一个抽象的流程,传入的都是形参,真实对象需要有一个具体的角色
代理对像可以做真实对象做不到的事情
真实对象可以专注自己的事情
五大状态
new -》就绪 ——》运行——》阻塞(可能)——》死亡
new:新生,创建线程对象
就绪:调用start方法时进入就绪状态
运行:不是立即运行
阻塞:调用Sleep wait等进入
死亡;线程中断
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4iHAyqMP-1619236330036)(…/…/AppData/Roaming/Typora/typora-user-images/image-20210423102834563.png)]
线程停止
推荐在main方法中加入控制语句来控制停止
线程休眠sleep
指定阻塞毫秒数
时间到达进入就绪状态
//延时模拟放大问题的发生性
10秒倒计时
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
TenDown(10);
}
static void TenDown(int a) throws InterruptedException {
int num = a;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0){
System.out.println("计时结束");
break;
}
}
}
}
yield 线程礼让
让cpu重新调度,不一定成功,运行状态转换为就绪状态
public class yield implements Runnable{
public static void main(String[] args) {
yield yield = new yield();
new Thread(yield,"a").start();
new Thread(yield,"b").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束执行");
}
}
b线程开始执行
a线程开始执行
a线程结束执行
b线程结束执行
a线程开始执行
a线程结束执行
b线程开始执行
b线程结束执行
强制执行join
例子。在主线程到20前,随即运行,但是主线程i达到20,调用join方法,会将子线程那个thread执行结束再运行主线程,会造成线程阻塞。
public class joinTest implements Runnable{
public static void main(String[] args) throws InterruptedException {
joinTest join = new joinTest();
Thread thread = new Thread(join);
thread.start();
for (int i = 0; i < 100; i++) {
if (i == 20){
thread.join();
}
System.out.println("主线程"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("子线程插队"+i);
}
}
}
线程状态观察
lambda创建线程
Thread thread = new Thread(()->{
for (int i = 0; i <= 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("");
});
线程观测,线程只能有一个start启动
Thread.State state = thread.getState();//创建这个线程监听
System.out.println(thread.getState());//输出这个状态,此时为new
thread.start();//启动线程进入Runnable
state = thread.getState();
while (state != Thread.State.TERMINATED){
System.out.println(state);
state = thread.getState();
Thread.sleep(1000);
}
}
线程优先级
优先级高的可能会先跑,但是不一定,具体看cpu调度
System.out.println(Thread.currentThread().getName()+"主线程优先级"+Thread.currentThread().getPriority());
mypro mypro = new mypro();
Thread t1 = new Thread(mypro);
Thread t2 = new Thread(mypro);
Thread t3 = new Thread(mypro);
Thread t4 = new Thread(mypro);
Thread t5 = new Thread(mypro);
t1.setPriority(1);
t1.start();
t2.setPriority(2);
t2.start();
守护线程
守护程序正常运行
设置方法 线程对象名字.setDemon(ture); 默认为false 为用户进程,ture为守护线程
liuyue liuyue = new liuyue();
snake snake = new snake();
Thread thread = new Thread(snake);
thread.setDaemon(true);
thread.start();
new Thread(liuyue).start();
线程同步
并发 多个线程同时操作一个对象
比如抢票
synchronized 同步方法
synchronized默认锁的是他本身,如果是方法里面的内容逻辑细节导致的,应该用synchronized块来锁内容,是一个监视器,难的是找到需要琐谁
方法头
synchronized(obj(要锁的对象)){
语句块
}
锁机制
修改内容才需要锁,锁会让效率变低
死锁
一个同步块有 两个以上对象的锁,可能会发生死锁,多个线程同时互相有对方需要的资源,形成僵持
Lock锁
final ReentrantLock lock = new ReentrantLock();
try {
lock.lock
}catch{}
finally{
lock.unlock
}
class ticket implements Runnable{
int Ticket = 10;
final ReentrantLock lock = new ReentrantLock();//定义Lock锁
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if (Ticket > 0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( Ticket--);
}else {
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//关锁
}
}
}
}
线程协作 生产,消费
这是一个线程同步问题
生产者和消费者共享同一个资源,并且生产者和消费者之
间相互依赖,互为条件.
对子生产,没有生产产品之前,要通知消费者等待﹒而生产了产品之后,又需要马上通知消费者消费
对于消费者﹐在消费之后,要通知生产者已经结束消费﹐需要生产新的产品以供消费.
在生产者消费者问题中﹐仅有synchronized是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
wait()
wait(long timeout)
notify()
notifyAll()
wait()
表示线程一直等待﹐直到其他线程通知,与sleep不同,会释放锁
wait(long timeout)
指定等待的毫秒数
notify()
唤醒一个处于等待状态的线程
notifyAll()
唤醒同一个对象上所有调用wait()方法的线程﹐优先级别高的线程优先调度
三种解决方法
- 管程法 建立缓冲区类
缓冲区就像一个容器,设置当生产者生产出来东西,容量加一,然后设置条件,
如果满了 通知消费者 如果没满 消费者等待
如果没满,放入生产者的产品
由消费者执行线程。
- 信号灯发 判断标志位 flag
线程池
能提高系统性能
提高响应速度,降低消耗,便于管理
创建使用 ExecutorService和Executors方法类
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
ExecutorService创建线程池
void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
void shutdown():关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
1lamda表达式
解决:匿名内部类过多的问题,使代码更加简洁
接口必须为函数式接口,一个接口只包含一个抽象方法,为函数式接口
由匿名内部类推到得出
构造器
接口 命名 = (参数)-> { 语句块};
ILike like4 = (接口定义的参数,没有为空) -> {
System.out.println();
};
like4.接口的方法
简化
多行代码
1。去掉参数类型 多个参数必须加括号
Love love = (a) ->{
System.out.println("123"+a);
};
love.love(2);
单个参数可以去掉括号
Love love2 = a ->{
System.out.println("123"+a);
};
love.love(2);
单个参数,只需要一句代码,可去掉大括号
Love love3 = a -> System.out.println("123"+a);
love.love(2);