Java线程

Java线程,第1张

自从用 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实现作为容器控制进程调度

线程会返回一个结果

优缺点
  1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

  2. 使用继承 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中并不是所有的代码都是互斥访问的,如果方法静态,那么进程中不互斥的代码无法同步执行

对方法块的处理

有时候你烦就烦在学完了给你介绍个更牛逼的

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/795265.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存