java-多线程与并发
java-多线程与并发
以下内容为本人的学习笔记,如需要转载,请声明原文链接 https://www.cnblogs.com/lyh1024/p/16786357.html
多线程
1.进程与线程
1.1 什么是进程
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
进程是一个具有一定独立功能的程序,一个实体,每一个进程都有它自己的地址空间。
1.2 进程的状态
进程执行时的间断性,决定了进程可能有多种状态。事实上,运行中的进程具有以下三种基本状态。
-
就绪状态(Ready)
-
运行状态(Running)
-
阻塞状态(Blocked)
cpu是有转速的,转速越快,性能越高。但CPU运行a程序时,要转到h程序,需要时间,这时候进程就会进入阻塞状态。
1.3 线程
线程实际上是在进程基础上调度进一步划分,一个线程启动后,里面的若干程序又可以划分成若干个线程。
线程:是进程中的一个执行路径,共享一个内存空间,程序之间可以自由切换,并发执行,一个进程最少有一个线程(单线程程序)
一个程序可以同时执行多个任务,来提高效率
例如:①同时下载多部电影
②看电影的同时吃零食
并行:就是两个任务同时运行(多个CPU)
并发:是指两个任务同时请求运行,而处理器一次只能接受一个任务,就会把两个任务安排轮流执行,由于CPU时间片运行时间较短,就会感觉是两个任务在同时执行
-
面试题:进程和线程的区别?
2.线程的基本使用
线程实现的三种方式
在java中如果要想实现多线程的操作,有两种实现方法:
1)继承Thread类(只能继承一个)
2)实现Runnable接口(建议使用,可以实现多个)
3)实现Callable接口
public class ThreadDeom1{
public static void main(String[] args){
MyThread mt = new Thread();
MyRunnable mr = new MyRunnable();
Thread t2 = new Thread(mr);//将实现Runnable的当成一个任务放进线程Thread里
mt.start();//启动线程,(实际是,start表准备就绪,告诉虚拟机可以启动线程)
t2.start();//可简写,合并成:new Thread(mr).start
}
}
/**
创建线程方式一:
1.继承Thread类
2.重写run()方法
3.创建线程对象,调用start()开启线程
注意:线程开启不一定立即执行,有CPU调度执行
*/
class MyThread extends Thread{
public void run(){
for(int i = 0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
/**
创建线程方式二:
1.实现Runnable接口
2.重写run()方法
3.创建Runnable接口的实现类对象
4.创建线程对象(代理),丢入实现类对象
5.调用start()开启线程
注意:线程开启不一定立即执行,有CPU调度执行
*/
class MyRunnable implement Runnable{
public void run(){
for(int i = 0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try{
/**
线程的休眠
在当前线程的执行中,暂停指定的毫秒数,释放CPU的时间片
释放CPU的时间片意思:CPU给一个进程P ,3秒执行时间,线程A和线程B互抢这个3秒时间片,设A抢到了,执行了3秒,此时会中断,然后CPU执行下一个进程,CPU一直转,又转到进程P,然后A和B又开始互抢这3秒时间片,A又抢到,继上次中断的位置开始执行,执行完后,就让时间片给其他线程。
*/
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
/**
创建线程方式三:
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务:ExecutorService ser = Exectors.newFixedThreadPool(1);
5.提交执行:Future<Boolean> result1 = ser.submit(t1);
6.获取结果:boolean r1 = result1.get();
7.关闭服务:ser.shutdownNow();
*/
案例:
3.线程的休眠
public static void sleep(long millis) throws InterruptedException
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),释放CPU的时间片,具体取决于系统定时器和调度程序的精度和准确性。 线程不会丢失任何显示器的所有权。
-
参数
millis
– 以毫秒为单位的睡眠时间长度 -
异常
IllegalArgumentException
– 如果millis
值为负数InterruptedException
– 如果任何线程中断当前线程。 当抛出此异常时,当前线程的中断状态将被清除。 由线程本身中断不会报错。
-
线程的休眠,目的是让出CPU执行的时间片,让其他工作的线程可以执行,但不会释放对象锁
-
sleep可以模拟网络延迟,倒计时等
-
sleep时间达到后线程进入就绪状态
public static void sleep(long millis,int nanos) throws InterruptedException//毫秒,纳秒
public static Thread currentThread()//返回对当前正在执行的线程对象的引用,即获取当前线程
4.join与中断线程
public final void join() throws InterruptedException
等待这个线程死亡。
调用此方法的行为方式与调用join(0) 完全相同
-
异常
InterruptedException
– 如果任何线程中断当前线程。 当抛出此异常时,当前线程的中断状态将被清除。
public void interrupt() | 中断这个线程。 除非当前线程中断自身,这是始终允许的 |
public static boolean interrupted() | 测试当前线程是否中断。 该方法可以清除线程的中断状态 。 换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false(除非当前线程再次中断,在第一个调用已经清除其中断状态之后,在第二个调用之前已经检查过)。 忽略线程中断,因为线程在中断时不存在将被该方法返回false所反映。 |
join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行
或者说 join合并线程,待此线程执行完毕后,回去再执行其他线程,其他线程阻塞,可以想象成插队
public class ThreadDeom2{
public static void main(String[] args){
MyRunnable mr2 = new MyRunnable2();
Thread t1 = new Thread(mr2);
MyRunnable mr3 = new MyRunnable3();
Thread t2 = new Thread(mr3);
t1.start();
t2.start();
for(int i = 0;i<50;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try{
Thread.sleep(300);
}catch (InterruptedException e){
e.printStackTrace();
}
if(i==20){
/** try{
t1.join();//让t线程执行完毕
}catch (InterruptedException e){
e.printStackTrace();
}*/
// t1.interrupt();//中断线程(不会真的中断线程),只是作了一个中断标记
mr3.flag =false;
}
}
}
}
class MyRunable2 implements Runnable{
public void run(){
for(int i = 0;i<50;i++){
/**
中断线程方式一:
1.使用interrupt()来中断线程,设置一个中断状态(标记)
2.run方法里测试中断状态Thread.interrupted()
3.有sleep等抛出InterruptedException异常的,要重新打上中断标记
中断线程方式二(更加推荐使用):
1.自定义标记的方式,设置一个布尔值为ture,作为while的条件,要中断时就将该布尔值设为false
*/
if(Thread.interrupted()){//测试中断状态,此方法会把中断状态清除
break;//不会真的中断,因为sleep会抛出InterruptedException异常并把中断标记清除,所以要重新打上中断标记
}
System.out.println(Thread.currentThread().getName()+"-"+i);
try{
Thread.sleep(300);
}catch (InterruptedException e){
e.printStackTrace();
Thread.currentThread().interrupt();//重新打上中断标记
}
}
}
}
class MyRunable3 implements Runnable{
public boolean flag = true;
public MyRunable3(){
flag = true;
}
public void run(){
int i = 0;
while(flag){
System.out.println(Thread.currentThread().getName()+"---"+i);
try{
Thread.sleep(300);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
5.守护线程与yield
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕(用户线程执行完毕后,JVM虚拟机自动退出,不用等待守护线程执行完毕)
如,后台记录操作日志,监控内存,垃圾回收等待….
method | 说明 |
---|---|
public final void setDaemon(boolean on) | 将此线程标记为daemon线程或用户线程。 当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。 |
public final boolean isDaemon() | 测试这个线程是否是守护线程。 |
public static void yield() | 暂停当前正在执行的线程对象,并执行其他线程(了解) |
yield作用是暂停当前正在执行的线程对象(放弃当前CPU资源),并执行其他线程。
yield是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会
补充:当源码由native修饰,表示为本地方法,由c或c++实现
for(int i = 0;i<50;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try{
Thread.sleep(300);
}catch (InterruptedException e){
e.printStackTrace();
}
if(i==5){
Thread.yield();//让出本次CPU执行时间片,就让一次,下一次还抢CPU时间片
}
}
6.其他方法与优先级
Method or Fieids | 说明 |
---|---|
long getId() |
返回此线程的标识符。 |
String getName() |
返回此线程的名称。 |
int getPriority() |
返回此线程的优先级。 |
boolean isAlive() |
测试这个线程是否处于活动状态,start以后就是活动状态。 |
void setName(String name) |
将此线程的名称更改为等于参数 name 。 |
void``setPriority(int newPriority) |
更改此线程的优先级。 |
static int MAX_PRIORITY |
线程可以拥有的最大优先级。 |
static int ``MIN_PRIORITY |
线程可以拥有的最小优先级。 |
static int NORM_PRIORITY |
分配给线程的默认优先级。 |
有待补充……
参考资料:
JDK1.8帮助文档
2022-10-12 22:22:12