自从用 Typora后,感觉csdn越来越…这学期以后更新随缘了,算法类为了督促自己还是会更新,不过现在真没啥时间,数暑假必刷算法
Java进程与线程
程序:是含有指令和数据的文件, 被存储在磁盘或其他的数据存储 设备中,也就是说程序是静态的 代码。
进程:是程序的一次执行过程, 是代码在数据集合上的一次运行 活动,是系统进行资源分配和调 度的基本单位。
进程是资源发配的独立单位
分配进程
线程是调度执行的独立单位
线程:是进程的一个执行路径, 一个进程中至少有一个线程,进 程中的多个线程共享进程的资源
线程并发完成进程速度更快,快速推进进程
JAVA对线程封装
currentThread()---获得当前CPU执行的线程
Class Thread //线程的属性
getId()返回线程的标识符
getName(返回线程名字)
getPriority()返回线程优先级
程序运行时变为进程,进程执行产生线程
进程一创建就会有堆和方法区会产生一个主线程,完成
package one;
public class Qq {
//
// public void LT()
// {
// for (int i = 1; i < 101; i++) {
// System.out.println(Thread.currentThread().getName()+":聊天进行进度"+i+"%");
// }
// }
// public void SC()
// {
// for (int i = 1; i < 101; i++) {
// System.out.println(Thread.currentThread().getName()+":上传进行进度"+i+"%");
// }
// }
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()); //
// Qq qq=new Qq();
//qq.LT();
LTThread ltThread=new LTThread();
ltThread.setName("聊天");//为进程填关键信息,区别进程名与类名
//ltThread.run();//不能真正调用线程,不用run方法按顺序调用
ltThread.start();//启动进程,等待分配CPU
SCThread scThread=new SCThread();
scThread.setName("上传");
scThread.start();
//qq.SC();
}
}
如果是Qq qq=new Qq();
qq.LT();
qq.SC();//则分批执行先聊天后上传
运行方法时分别调用了什么进程?都是调用主线程,一个进程解决
如何实现同时?并行 *** 作
可以让聊天单独用一个线程这样就可以交替的
在原有进程的基础下创建子类进程
package one;
public class LTThread extends Thread{//线程派生子类,如何体现执行?相当于把这个程序分配到一个容器中,容器自动管理
@Override
public void run() {//重写线程run方法,线程执行什么写在这里,我们不调用聊天方法,调用一个聊天线程类
for (int i = 1; i < 101; i++) {
System.out.println(Thread.currentThread().getName()+":聊天进行进度"+i+"%");
}
}
}
创建线程
继承的方法创建线程
package two;
public class T1 extends Thread{
public T1(String name) {
super(name);//父类含有一个传参数构造方法,直接传入名字,不可继承,可以调用
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(this.getName());
System.out.println("我是新的线程");
}
}
package two;
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
T1 t1=new T1("new");
//t1.setName("new");
t1.setPriority(10);//设置优先级,调度的参考
t1.start();
//t1.start();//一个类对象只能启动一次线程
}
}
实现接口创建进程
package two;
public class T2 implements Runnable{
public static int a=100;
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+a--);//看看哪个线程在执行
}
}
}//对实现接口的线程类来说,需要进行封装
package two;
public class Test2 {
public static void main(String[] args) {
T2 t2=new T2();
Thread thread=new Thread(t2);//这种类可以接收符合这种标准的任何对象,接口中无start功能,但是线程类有,传入一个接口类对象,得到一个线程类
第二次思考--接收一个含有具体实现方法的接口,让之成为进程
// Thread thread=new Thread(t2,"new");//传入一个具体的运行接口,和该进程的名字
thread.start();//start分配是否run
T2 t3=new T2();
Thread thread1=new Thread(t3);
// Thread thread1=new Thread(t3,"new1");
thread1.start();//如果类名相同,注意进程 *** 控的类变量如何
}
}
使用Callable和Future创建线程
package two;
import java.util.concurrent.Callable;
public class T3 implements Callable<Integer> { //接口返回一个对象类型为Integer类型,但本身应具有线程最基本的方法体,我们也需要引入一个接口可以接收返回值并作为进程执行
///run call 都是线程执行时调用的方法体
//不同的是 call 有返回值 并且有异常抛出
@Override
public Integer call() throws Exception {//对这个方法体判断是否抛出异常,进程运行出错,抛出异常
System.out.println(Thread.currentThread().getName());
int sum=0;
for(int i=1;i<11;i++)
sum+=i;
return sum;//可以直接转换为类么?
}
}
package two;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test3 {
///可以有返回值的
public static void main(String[] args) throws InterruptedException, ExecutionException {//里面出错抛出异常
// TODO Auto-generated method stub
T3 t3=new T3();
FutureTask<Integer> futureTas=new FutureTask<Integer>(t3);//作为参数传入封装类直接封装,具有线程执行功能即start,构造方法传入一个Callable进程基本接口实现类,注意这个类的泛型变化,与方法体变化
第一个引入接口得到返回值类型,方法体作为进程的基础,该返回值用于进程结束的一些 *** 作
然后作为参数传入接口,具有进程功能,和获取返回值的能力,获取进程自身的返回值的能力,最后作为参数传入Thread主接口,实现具有获取自身返回值的进程功能
Thread thread=new Thread(futureTas,"test1");//
thread.start();
system.out.println(futureTas.get());
System.out.println(futureTas.get());
Thread thread1=new Thread(futureTas,"test2");//
thread1.start();
System.out.println(futureTas.get());//因为进程运行时间可能很长,必须要有异常处理
}
}
如果对象需要返回值,那么这种进程只会执行第一个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
线程一执行就调度这种方法—call–线程执行体
使用Future Task类包装这种线程执行类,该接口封装了Callable对象的call()方法的返回值,这个接口取得返回值,而RunnableFuture 实现了线程接口与可取返回值的接口
第二次思考–实现一个具有返回值的接口,作为进程方法体,第二个接口Future Task包装方法与返回值,进程接口Thread实现作为容器控制进程调度
线程会返回一个结果
优缺点-
采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
-
使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
继承方式,单继承,无法继承其他类
接口式–解决多继承
需要返回值与不使用返回值
进程的几种特殊实现方式
package two;
public class Test4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread() {@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是新的线程");
}
}.start();
}
} 线程类中匿名子类,重写run方法,以匿名的方式实现线程
package two;
public class Test5 {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是新的线程");
}
}).start();//直接传入一个接口,重写该接口
}
}
匿名接口形式
package two;
public class Test6 {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(()-> {System.out.println("我是新的线程");}).start();//
}
}
线程直接接收方法体内容
package two;
public class Test7 extends Thread{
public static void main(String[] args) {
// TODO Auto-generated method stub
Test7 test7=new Test7();
test7.start();//线程测试类 非重点
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是新线程");
}
}
package two;
public class Test8 implements Runnable
{public static void main(String[] args) {
// TODO Auto-generated method stub
Test8 test8=new Test8();
Thread t1=new Thread(test8); //测试接口将接口传入进程类中
t1.start();
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是新的线程");
}
}
线程生命周期
先实例化线程对象,start
就绪状态 —除了cpu的所有资源
获得CPU转向运行状态,单核指cpu个数
运行状态–线程的死亡或者运行中出现异常线程也结束
运行到就绪,跳转,比如计组中中断
阻塞态,暂时缺乏什么设备,到了后到达就绪态
线程生命周期转换过程并发每次结果不一样
还记得搞笑的睡眠排序
package four;
public class T2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
Long s=System.currentTimeMillis();//记录开始时间
System.out.println("开始了");
try {
Thread.sleep(2000);//毫秒 这个异常不能抛出不处理,只能在本地进行处理,选择抓取的语句,捕捉异常
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("结束了");
System.out.println("用时毫秒:"+ (System.currentTimeMillis()-s));//休眠的时间
}
}
package four;
public class T3 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 1; i < 100; i++) {
System.out.println(i);
}
}
}
package four;
public class Test3 {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
T3 t3=new T3();
Thread thread=new Thread(t3,"测试");
thread.start();
thread.join();
System.out.println("main线程结束了");
}
}
如果没有join那肯定在分配cpu之前就输出main线程结束语句了
线程的同步问题
进程完成了一些对线程的调度控制—真跟K8S那样啊 容器
还使用进程中的一些共享变量
会出现什么问题?
一个共享变量被同时使用或写入时,会出现什么问题?
线程1失去cpu–
线程三顶上
注意线程1中的私有值,多个线程并发访问引发脏数据
立刻写入cpu让其它进程中副本数据立刻清空,必须从主内存中取得新数据
package three;
public class TestVo extends Thread{
// public volatile static boolean stop=false; //加上就会通知其它副本清零后使用变化后的这个变量,然后就能正常结束了
public static boolean stop=false;
@Override
public void run() {
System.out.println("我开始了");
while (!stop) {//拷贝副本
}
System.out.println("我结束了");
}
public static void main(String[] args) throws InterruptedException {
TestVo testVo=new TestVo();
testVo.start();
//Thread.sleep(2000);//主线程休息2秒后再修改值,这么搞我们在子线程运行时才修改stop,没有通知到子进程中的副本
stop=true;
}
}
分析stop是共享变量,打开主线程会让stop变为true子线程结束
原子性问题
语句分为好几个指令,会有中断现象,a+1为好几个指令,中断失去cpu
中断应保护现场
两个线程 因为cpu切换和原子性指令都读出a=10,结果算出11
写误差
如何解决原子性问题
临界区只能由一个线程进入
所谓临界资源 因中断必须保护现场的
比如访问a
锁机 开锁与关锁,只有一个进程能拿到锁,不用释放
package three;
public class T1 implements Runnable {
// Object object=new Object();
static Object object = new Object(); //工程中定义一个对象然后锁住这个对象的方法体,或者这里是定义一个静态对象也就是类,锁住这个类
synchronized public void f1() {//思考怎么让f1只能让一个进程进去--这个锁指的是当前实例对象,加上staic直接变为类锁
System.out.println("测试"); //测试语句同步进行
synchronized (object) { //对方法块的锁,这样我们就可以锁住我们需要互斥的了,(this)代表本对象这个方法的互斥访问 (T1.class)代表整个类的互斥,这里是定义一个对象,锁住这个对象,
System.out.println(Thread.currentThread().getName() +"开始了");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束了");
}
}
@Override
public void run() {
f1();
}
}
//static Object object=new Object();
下面对应
synchronized (object){//锁住类,这样的话t与tt属于同一object(所有类的父类)注意这个方法是在同一接口中。
//申请一个object静态类,所有用这个方法的对象,都要获得这个静态类的锁,(相当于有一个房子,它的钥匙在邻居手里,是唯一的一把,其它类对象访问家只能找邻居要钥匙,只能有一个人进入家)相当于把那些要用这个方法的对象互斥了,因为这个静态类的锁是整个共有的,只有一把
}
package three;
public class Testsy {
public static void main(String[] args) {
T1 t=new T1(); //方法所在实例对象为锁
T1 tt=new T1();
Thread t1=new Thread(t,"测试1");
Thread t2=new Thread(t,"测试2");
Thread t3=new Thread(tt,"测试3");
t1.start();//锁住
t2.start();//要不到锁,f1中不允许同时有两个线程
t3.start();//这个锁是 tt的实例对象,是tt对象中f1的锁,可以执行
}
}
怎么锁住整体,让f1变为静态方法,不是针对对象要锁,针对类要锁
那么如果不整体互斥该怎么办?就是在f1中并不是所有的代码都是互斥访问的,如果方法静态,那么进程中不互斥的代码无法同步执行
对方法块的处理
有时候你烦就烦在学完了给你介绍个更牛逼的
只能找邻居要钥匙,只能有一个人进入家)相当于把那些要用这个方法的对象互斥了,因为这个静态类的锁是整个共有的,只有一把
}
```java
package three;
public class Testsy {
public static void main(String[] args) {
T1 t=new T1(); //方法所在实例对象为锁
T1 tt=new T1();
Thread t1=new Thread(t,"测试1");
Thread t2=new Thread(t,"测试2");
Thread t3=new Thread(tt,"测试3");
t1.start();//锁住
t2.start();//要不到锁,f1中不允许同时有两个线程
t3.start();//这个锁是 tt的实例对象,是tt对象中f1的锁,可以执行
}
}
怎么锁住整体,让f1变为静态方法,不是针对对象要锁,针对类要锁
那么如果不整体互斥该怎么办?就是在f1中并不是所有的代码都是互斥访问的,如果方法静态,那么进程中不互斥的代码无法同步执行
对方法块的处理
有时候你烦就烦在学完了给你介绍个更牛逼的
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)