java-多线程与并发

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

 

 

 

 

 

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » java-多线程与并发