文章目录
8.1线程概述
程序、进程、线程
一组静态代码
当程序获得cpu计算资源是不叫程序了,叫进程,动态的
是永远有资源的单位
就是一个执行机构
线程:进程,多对一
对应关系:
进程是分配的独立单位
线程是调度执行的独立单位
线程执行进程的任务,进程提供资源,组合起来完成任务
进程和线城是动态的,进程是拥有资源的单位,作为线程的后盾,线程来执行
相同一件事情,分的越多,并发,执行效率更高 ,也就是多线程
在java中对线程也进行了封装,class
id name 优先级(cpu调度)
多线程含义
main是主线程
会创建主线程
模拟QQ聊天和上传(实际上还是串行,但很快,就像并行)
实际都是主线程进行的:,根本没并行啊,
希望聊天单独生成一个线程,用户角度就并行了
用继承创建一个自己的新进程类
重写方法run,要进行什么都写到run里
下一步在创建聊天线程,并run(执行)
失败了:
不要使用run方法,线程执行时会自动调用,要好多事情呢(资源分配。。。。。)
调用的话,就是普通调用普通方法
启动线程:
效果:(默认名)
重置名字:所有属性都要在 start之前完成:
上传线程同理:
ONE文件夹下
package one;
public class Qq {
public Qq() {
}
public void LT() {
}
public void SC() {
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
new Qq();
LTThread ltThread = new LTThread();
ltThread.setName("����");
ltThread.start();
SCThread scThread = new SCThread();
scThread.setName("�ϴ�");
scThread.start();
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package one;
public class Test1 {
public Test1() {
}
public static void main(String[] args) {
System.out.println("我是测试程序");
System.out.println(Thread.currentThread().getId());
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getPriority());
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package one;
public class SCThread extends Thread {
public SCThread() {
}
public void run() {
for(int i = 1; i < 101; ++i) {
System.out.println(Thread.currentThread().getName() + ":上传进行进度" + i + "%");
}
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package one;
public class LTThread extends Thread {
public LTThread() {
}
public void run() {
for(int i = 1; i < 101; ++i) {
System.out.println(Thread.currentThread().getName() + ":��������" + i + "%");
}
}
}
8.2线程的创建
不管是继承还是实现创建,在实例化时都可以塞到Thread tread =new Tread(a,“name”)里实现变量共享(如下链接举例)
继承Thread创建
线程创建时相关接口与具体类:
上节的做法
一定要重写run方法:
测试:new时未创建,只是实例化一个对象,start时才启动(分配资源,创建线程,执行)
启动两次?:异常:只能启动一次
还可以设置各种属性:但要在启动之前,
优先级:在java虚拟机中的优先策略,但在cpu中还会自己参考着重新考虑吧
还可以重搞构造方法:(不能重搞,但可继承)
还可获取当前线程的属性:
两个写法,效果一样:
实现接口:Runnable创建
只要是线程就必须实现接口,实现run方法
实现所有方法,但只有run方法
只实现了接口,没有start方法,要运行就要先封装
测试类
封装
名字是new
好处:创建相同对象,共享类中变量,但此时不同步
共享(t2,t3中的a不同)
new的a从100开始,new1的a也从100开始
共享静态变量,即可同步
Callable和Future创建
很少用,没讲
Callable,call方法有个返回值
用future接口取返回值:
RunnableFuture同时符合两个
所以用FutrueTask
V是泛型
要实现抽象方法call
一个接口实现一个接口,又实现一个接口(向上套娃)
先实现接口,并实现方法重写,
10:创建工具类,并从t3中取得call方法的值,实现了相应线程的接口
11:实现接口,封装
12:执行
可以获取返回值,可能等待较长出现异常
下图中的return
用get获取
法一 try catch
法二:抛出
summary
一些特殊创建写法 比较
java是单继承的,使用继承的方式,就无法继承其他类了,继承层次就会很深,但接口可以多重,工程中实现接口的会比较多
匿名内部类
UI界面开发有可能会用
类的匿名
接口匿名实现
Thread()中间写接口
lambda表达式创建线程
线程里接受一个函数表达式
常见方法
测试类直接继承线程(一般用来测试)
测试类直接实现接口(一般用来测试)
8.3线程生命周期
8.3.1生命周期描述
进程拥有资源,执行调度
线程执行过程,有状态转换
新建状态:实例化
就绪状态:创建了线程、得到了资源,得到了除了cpu的所有资源
运行状态:在cpu中运行。(单核只能有一个)
分支:阻塞状态:除了cpu还缺其他资源,不能进行,所以就先失去cpu给别人,你先等着(比如缺打印机)
分支:DEAD:异常了、完成了、人为停止
分支:回就绪状态:听从cpu调度
8.3.2Thread常用方法(怎么实现上图)
线程类提供的常用方法
启动
并不是立即执行
让步方法
并发
只让出一次cpu
休眠,暂停
自动休眠与阻塞
可能会产生异常,
没有返回值不能抛出,只能try catch
等待线程结束
主线程先结束了,不合逻辑
这里用了抛出异常
thrad执行完,才能往下执行
成对,阻塞,需要同步问题,到时讲
8.4线程同步
进程是资源的后盾
线程是调度的单位
Java线程是共享进程中的资源
创建副本
比如A和B都想进行加1操作,原为3,A先将3拷贝,突然失去cpu,B拷贝+1,变4,将4 回写 到变量1,A重获的cpu,副本3+1回写4
最终变量一为4,可希望为5
引出问题:线程同步
第一种解决方案:
线程同步-可见性(非线程安全)
1.立刻
2.强制
3.是所有缓存了该变量的线程中的数据清空
可见性:有人改了,其他线程也该知道
验证:
由stop决定是否结束:()用环境变量达到同样的效果
修改:修改为真:
检测是否为共享变量(主线程,与testVo)
法二:让主线程休息2秒再修改:
不结束了,因为副本已经建立起来了,没通知修改,不可见
关键字的使用
线程同步-原子性问题
之前没解决()
还是都进行a+1操作
这些指令是可以中断的
中断时要保护线程,恢复是要恢复
希望不可被中断
锁机制:
区别在锁的类型:
测试:
看看执行f1的线程是谁,并休眠他
希望f1()同时只能被一个访问
想访问f1得先有锁
向t对象要锁
tt对象和t对象独立
另:多个线程共同执行同一个方法的情况,线程与runnnable对象
tt也在使用f1
t1 执行f1,向t要锁
t2也想要t的锁,等待
t3向tt要锁,所以可以执行
刚才是锁住同一个对象
如果对静态方法上锁,那就是整个类的锁
执行时,向类要锁
保证没有任何线程干扰
也不是特别公平
方法内有些代码不用锁
这句话就不用
所以用方法块
这样就不知道对类还是对象获取了
加上本对象要锁
对t1,t2 互斥
以类获取锁
也是以对象获锁,因为同一个对象内的成员对象才相同
这样就是类了(t,tt中的object相同)
lock接口
新的锁机制
实现类
区别:人为释放,finally中释放
老例子了:
要先建立锁对象
上锁:
try:
finally
还锁:
wait()和notify()
阻塞方法,和唤醒方法
(sleep是自动唤醒)
wait是被动唤醒
由object提供
测试类:
新建一个线程,休眠两秒,继续执行
在搞一个一样的
希望一个工作时另一个等待
互斥访问
想到同步代码块
每个都加上同步锁
对同一对象获取锁
两个都是
希望线程一执行-》停止-》二执行-》结束-》线程一
sleep休眠不放弃锁
wait放弃锁
线程1(thread0)一直在阻塞
线程2结束时唤醒1
区别
obj指上锁的那个对象
8.5线程池
GC:垃圾回收线程
线程池的概念
整体管理线程的机制,把握创建线程的度
core核心线程池:不能停
其他不工作就撤
没申请到就异常
有的不销毁,重新用
线程池相关类
四个构造方法
第一个参数:
简单线程池类
肥宅憨憨池
会有返回值:
交给她管理
搞五个
max为5,(效果不太好,任务太短了)
这个例子是不是太好了???,难道不会有重复的id嘛
不是id的问题
name也会
A1:me:那他是先都加入到线程池然后执行的嘛 都在线程池中
me:啊,那就是说5个时,先往里加,然后在线程池里执行,10个的时候,先加5个,等有执行完了,再往里加,然后10个的那个重了好几次,就是有后加进去的先执行完了
A2:线程池线程复用原理
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// System.out.println(121212)
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i=0;i<10;i++)
{
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
}
}
eg
pool-1-thread-5
pool-1-thread-4
pool-1-thread-3
pool-1-thread-1
pool-1-thread-5
pool-1-thread-1
pool-1-thread-5
pool-1-thread-2
pool-1-thread-5
pool-1-thread-1
A1:应该是对的:我尝试睡几秒,睡前睡后分开输出,但一开始的5个都没有线程被让出:
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// System.out.println(121212)
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i=0;i<10;i++)
{
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开啦");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"停啦error");
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"停啦2");
}
});
}
}
}
output:
pool-1-thread-2开啦
pool-1-thread-3开啦
pool-1-thread-4开啦
pool-1-thread-5开啦
pool-1-thread-1开啦
pool-1-thread-2停啦2
pool-1-thread-4停啦2
pool-1-thread-2开啦
pool-1-thread-4开啦
pool-1-thread-5停啦2
pool-1-thread-1停啦2
pool-1-thread-5开啦
pool-1-thread-3停啦2
pool-1-thread-1开啦
pool-1-thread-3开啦
pool-1-thread-4停啦2
pool-1-thread-3停啦2
pool-1-thread-1停啦2
pool-1-thread-5停啦2
pool-1-thread-2停啦2
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// System.out.println(121212)
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i=0;i<5;i++)
{
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开啦");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"停啦error");
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"停啦2");
}
});
}
}
}
pool-1-thread-1开啦
pool-1-thread-3开啦
pool-1-thread-2开啦
pool-1-thread-5开啦
pool-1-thread-4开啦
pool-1-thread-5停啦2
pool-1-thread-1停啦2
pool-1-thread-4停啦2
pool-1-thread-3停啦2
pool-1-thread-2停啦2
更诡异的情况:
本想看看(这样发现,是一起加进去的,但等着)
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// System.out.println(121212)
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i=0;i<10;i++)
{
System.out.println(" thisloop:"+i);
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开啦");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"停啦error");
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"停啦2");
}
});
}
}
}
thisloop:0
thisloop:1
thisloop:2
thisloop:3
thisloop:4
thisloop:5
thisloop:6
thisloop:7
thisloop:8
thisloop:9
pool-1-thread-4开啦
pool-1-thread-3开啦
pool-1-thread-1开啦
pool-1-thread-2开啦
pool-1-thread-5开啦
pool-1-thread-3停啦2
pool-1-thread-4停啦2
pool-1-thread-1停啦2
pool-1-thread-2停啦2
pool-1-thread-4开啦
pool-1-thread-2开啦
pool-1-thread-5停啦2
pool-1-thread-5开啦
pool-1-thread-3开啦
pool-1-thread-1开啦
pool-1-thread-4停啦2
pool-1-thread-2停啦2
pool-1-thread-5停啦2
pool-1-thread-1停啦2
pool-1-thread-3停啦2
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// System.out.println(121212)
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i=0;i<10;i++)
{
System.out.println(Thread.currentThread().getName()+" thisloop:"+i);
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开啦");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"停啦error");
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"停啦2");
}
});
}
}
}
误会,是巧合,,,,,,(我以为加上crrunentThread后就五个一起开一起关了呢)
out1
main thisloop:0
main thisloop:1
main thisloop:2
main thisloop:3
main thisloop:4
main thisloop:5
main thisloop:6
main thisloop:7
main thisloop:8
main thisloop:9
pool-1-thread-3开啦
pool-1-thread-4开啦
pool-1-thread-2开啦
pool-1-thread-5开啦
pool-1-thread-1开啦
pool-1-thread-2停啦2
pool-1-thread-1停啦2
pool-1-thread-4停啦2
pool-1-thread-3停啦2
pool-1-thread-5停啦2
pool-1-thread-2开啦
pool-1-thread-3开啦
pool-1-thread-5开啦
pool-1-thread-4开啦
pool-1-thread-1开啦
pool-1-thread-2停啦2
pool-1-thread-5停啦2
pool-1-thread-3停啦2
pool-1-thread-4停啦2
pool-1-thread-1停啦2
out2
main thisloop:0
main thisloop:1
main thisloop:2
main thisloop:3
main thisloop:4
main thisloop:5
main thisloop:6
main thisloop:7
main thisloop:8
main thisloop:9
pool-1-thread-4开啦
pool-1-thread-2开啦
pool-1-thread-5开啦
pool-1-thread-1开啦
pool-1-thread-3开啦
pool-1-thread-2停啦2
pool-1-thread-2开啦
pool-1-thread-5停啦2
pool-1-thread-3停啦2
pool-1-thread-5开啦
pool-1-thread-4停啦2
pool-1-thread-4开啦
pool-1-thread-1停啦2
pool-1-thread-1开啦
pool-1-thread-3开啦
pool-1-thread-2停啦2
pool-1-thread-5停啦2
pool-1-thread-4停啦2
pool-1-thread-1停啦2
pool-1-thread-3停啦2
而且主线程和他们之间也有调度,都在池里?
main thisloop:0
main thisloop:1
main thisloop:2
main thisloop:3
main thisloop:4
main thisloop:5
main thisloop:6
main thisloop:7
main thisloop:8
pool-1-thread-1开啦
pool-1-thread-4开啦
pool-1-thread-2开啦
main thisloop:9
pool-1-thread-5开啦
pool-1-thread-3开啦
pool-1-thread-4停啦2
pool-1-thread-5停啦2
pool-1-thread-2停啦2
pool-1-thread-1停啦2
pool-1-thread-4开啦
pool-1-thread-3停啦2
pool-1-thread-3开啦
pool-1-thread-2开啦
pool-1-thread-1开啦
pool-1-thread-5开啦
pool-1-thread-4停啦2
pool-1-thread-3停啦2
pool-1-thread-1停啦2
pool-1-thread-5停啦2
pool-1-thread-2停啦2
当给主线程加上100ms等待:
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// System.out.println(121212)
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i=0;i<10;i++)
{
System.out.println(Thread.currentThread().getName()+" thisloop:"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开啦");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"停啦error");
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"停啦2");
}
});
}
}
}
main thisloop:0
main thisloop:1
pool-1-thread-1开啦
main thisloop:2
pool-1-thread-2开啦
main thisloop:3
pool-1-thread-3开啦
main thisloop:4
pool-1-thread-4开啦
main thisloop:5
pool-1-thread-5开啦
main thisloop:6
main thisloop:7
main thisloop:8
main thisloop:9
pool-1-thread-1停啦2
pool-1-thread-1开啦
pool-1-thread-2停啦2
pool-1-thread-2开啦
pool-1-thread-3停啦2
pool-1-thread-3开啦
pool-1-thread-4停啦2
pool-1-thread-4开啦
pool-1-thread-5停啦2
pool-1-thread-5开啦
pool-1-thread-1停啦2
pool-1-thread-2停啦2
pool-1-thread-3停啦2
pool-1-thread-4停啦2
pool-1-thread-5停啦2