自学javaSE-多线程

多线程

一、多线程的基本概念

1、什么是进程

​ 一个进程对应一个应用程序。例如:在windows操作系统启动Word就代表启动了一个进程。在java环境下启动JVM,就代表启动了一个进程。现代的计算机都是支持多进程的,在同一个操作系统中国,可以同时启动多个进程。

2、多进程有什么作用

​ 单进程计算机只能做意见事情。

​ 玩电脑,一边玩游戏(游戏进程)一边听音乐(音乐进程)

​ 对于单核计算机来讲,在同一个时间点上,游戏进程和音乐进程事同时运行的吗?不是。

因为计算机的cpu只能在某一个时间点上做一件事情,由于计算机将在“游戏进程”和"音乐进程"之间频繁的切换执行,切换速度极高,人类感觉游戏和音乐在同时执行。

​ 多进程的作用不是提高执行速度,而是提高CPU的使用率

进程和进程之间的内存是独立的

3、什么是线程

​ 线程是一个进程中的执行场景,一个进程可以启动多个线程。

4、多线程有什么作用

​ 多线程不是为了提高执行速度,而是为了提高应用程序的使用率

​ 线程和线程共享“堆内存和方法区内存”,栈内存是独立的,一个线程一个栈

5、java 程序的运行原理

​ java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,表示启动了一个进程,该进程会启动一个“主线程”,然后主线程去调用某个类的main方法,所以main方法运行在主线程中。在此之前所有的程序都是单线程的。

二、线程的创建和启动

image-20200910170421609

/*
	在java语言中实现多线程的第一种方式:
		第一步:继承java.lang.Thread;
		第二步:重写run方法
	三个知识点:
		如何定义线程
		如何创建线程
		如何调用线程
		
*/
public class ThreadTest02{
    public static void main(Stirng[] args){
        
        //创建线程
        Thread t = new Processor();
        
        //启动
        t.start(); 	//这段代码执行瞬间结束,告诉JVM再分配一个新的栈给t线程
        			//run不需要程序员手动调用,系统线程启动之后自动调用run方法
        //t.run(); //这是一个普通的方法调用,这样的程序只有一个线程,run方法结束之后,下面的程序才能继续执行
        
        //这段代码在主线程中执行
        for(int i = 0;i<10;i++){
            System.out.println("main-->"+i);
        }
        
        //有了多线程之后,main方法结束只是主线程中没有方法栈帧了
        //但是其他线程或其他栈中还有栈帧
        //main方法结束,程序可能还在运行
        
    }
}
//定义一个线程
class Processor extends Thread{
    
    //重写run方法
    public void run(){
        for(int i = 0;i<100;i++){
            System.out.println("run-->" + i);
        }
    }
    
}
/*
	java中实现线程的第二种方式:
		第一步:写一个类实现java.lang.Runnable;接口
		第二部:实现run()方法
*/
public class ThreadTest03{
    
    public static void main(String[] args){
        
        //创建线程
        Thread t = new Thread(new Processor());
        
        //启动
        t.start();
        
    }
    
}

//定义线程(推荐使用这种方式)
class Processor implements Runnable{
    public class run(){
        for(int i = 0;i<10;i++){
            System.out.println("run-->"+i);
        }
    }
}

三、线程的生命周期

image-20200910192935703

四、线程的调度与控制

通常我们的计算机只有一个cpu,cpu在某一个时刻只能执行一条指令,线程只有得到cpu的时间片,也就是使用权,才能执行指令。在单cpu的机器上,线程不是并行运行的,只有在多个cpu上线程才可以并行运行。java虚拟机要负责线程的调度,取得cpu的使用权,目前有两种调度模型:分时调度模型和抢占式调度模型,java使用抢占式调度模型。

分时调度模型:所有线程轮流使用cpu的使用权,平均分配每个线程使用cpu的时间片

抢占式调度模型:给优先级高的线程分配相对比较多的时间片,如果线程的优先级相同,那么会随机分配时间片

/*
	Thread的三个方法
	1、获取当前线程对象
		Thread.currentThread();
	2、获取线程名字与给线程起名字
		Thread.currentThread().getName();
		Thread.currentThread().setName();
	3、重新设置线程优先级(默认(NORM_PRIORITY)是5,最小(MIN_PRIORITY)1,最大(MAX_PRIORITY)10)
		Thread.currentThread().setPriority(1~10);
		
*/

sleep

/*
	1、Thread.sleep(毫秒);
	2、sleep方法是一个静态方法
	3、该方法的作用:阻塞当前线程,腾出CPU,让给其他线程
*/
public class ThreadTest06{
    public static void main(String[] args){
        Thread t1 = new Processor();
        t1.setName("t1");
        t1.start();
        
        //阻塞主线程
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName+"--->"+i);
            Thread.sleep(500);//这个sleep方法是一个静态方法,所以永远指向的是当前线程
        
        
    }
}

//定义线程
class Processor extends Thread{
    //Thread 中的run方法不抛出异常,所以重写run方法之后,在run方法的声明位置上不能使用throws
    //所以run方法中的异常只能try。。。catch。。
    public void run(){
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
            try{
                Thread.sleep(1000);//让当前线程阻塞1秒
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
/*
	某线程正在休眠,如何打断它的休眠
	以下方式依靠的是异常处理机制
*/
public class ThreadTest08{
    public static void main(String[] args){
        //需求:启动线程,5秒之后打断线程的休眠
        Thread t = new Thread(new Processor());

        //起名
        t.setName("t");

        //启动
        t.start();

        //5s之后
        Thread.sleep(5000);

        //打断t的休眠
        t.interrupt();
    }
}
class Processor implements Runnable{
    public void run(){
        try{
            Thread.sleep(1000000000000L);
            System.out.println("HelloWorld");
        }catch(InterruptedException e){
            //e.printStackTrace();
            
        }
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread.getName()+"--->"+i);
        }
    }
}
/*
	如何正确的更好的终止一个正在执行的线程
	需求:线程启动后5s之后终止。
*/
public class ThreadTest09{
    public static void main(String[] args){
        Processor p = new Processor();
        Thread t = new Thread(p);
        t.setName("t");
        //5秒之后终止
        t.sleep(5000);
        //终止
        p.run = false;
        
        
        
    }
}
class Processor implements Runnable{
    boolean run = true;
    public void run(){
        for(int i = 0;i<10;i++){
            if(run){
                try{Thread.sleep(1000);}catch(Exception e){}
                System.out.println(Thread.currentThread().getName()+"-->"+i);
            }else{
                return;
            }
        }
    }
}

yield

/*
	Thread.yield();
	1、该方法是一个静态方法
	2、作用:给同一优先级的线程让位,但是让位时间不固定
	3、和sleep方法相同,就是yield时间不固定
*/
public class ThreadTest10{
    
    public static void main(String[] args){
        
        Thread t = new Processor();
        
        t.setName("t");
        
        t.start();
        
        //主线程
        for(int i = 0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
        
    }
    
}

class Processor{
    public void run(){
        for(int i = 0;i<100;i++){
            System.out.println(Thread.currentThrad().getName()+"-->"+i);
            if(i%20==0){
                Thread.yield();
            }
        }
    }
}

join(成员方法)

/*
	线程的合并
*/
public class ThreadTest11{
    
    public static void main(String[] args){
        Thread t = new Thread(new Processor());
        t.setName("t");
        t.start();
        //合并线程
        t.join();//将t线程合并到主线程
        //主线程
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
        
        
    }
    
}

class Processor implements Runnable{
    public void run(){
        for(int i = 0;i<5;i++){
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                
            }
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

五、线程的同步(加锁)

未使用线程安全机制

/*
	t1 t2两个线程
	异步编程模型:t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁
	同步编程模型:t1线程和t2线程执行,t1线程必须等t2线程执行完毕后才能执行
	
	什么时候要同步呢?为什么要引入线程同步?
		1、为了数据的安全。尽管应用程序的效率减低了,但是为了保证数据是安全的,必须使用线程同步
		2、什么条件下使用线程同步
			第一:必须是多线程环境下
			第二:多线程环境共享一个数据
			第三:共享数据涉及到修改操作
	以下程序演示取款例子,以下程序不使用线程同步机制,多线程同时对一个账户进行取款,会出现什么问题?
*/
public class ThreadTest12{
    
    public static void main(String[] args){
        
        //创建一个公共的账户
        Account act  = new Account("actno-001",5000.0);
        
        //创建线程对同一个账户取款
        Thread t1 = new Thread(new Processor(act));
        Thread t2 = new Thread(new Processor(act)); 
        
        t1.start();
        t2.start();
        
    }
    
}

//取款线程
class Processor implements Runnable{
    
    //账户
    Account act;
    
    //Constructor
    Processor(Account act){
        this.act = act;
    }
    
    public void run(){
        act.withdraw(1000.0);
        System.out.println("取款1000.0成功,余额:"+act.getBalance());
    }
    
}
//账户
class Account{
    private String actno;
    private double balance;
    
    //Constructor
    public Account(){}
    public Account(String actno,double balance){
        this.actno = actno;
        this.balance = balance;
    }
    //setter and getter
    public void setActno(String actno){
        this.actno = actno;
    }
    public void setBalance(double balance){
        this.balance = balance;
    }
    public String getActno(){
        return actno;
    }
    public double getBalance(){
        return balance;
    }
    
    //对外提供一个取款的方法
    public void withdraw(double money){//对当前账户进行取款操作
        double after = balance - money;
        //延迟
        try{Thread.sleep(1000)}catch(Exception e){}
        //更新
        this.setBalance(after);
    }
}

使用线程安全机制

/*
	以下程序使用线程同步机制保证数据安全
*/
public class ThreadTest13{
    
    public static void main(String[] args){
        
        //创建一个公共的账户
        Account act  = new Account("actno-001",5000.0);
        
        //创建线程对同一个账户取款
        Thread t1 = new Thread(new Processor(act));
        Thread t2 = new Thread(new Processor(act)); 
        
        t1.start();
        t2.start();
        
    }
    
}

//取款线程
class Processor implements Runnable{
    
    //账户
    Account act;
    
    //Constructor
    Processor(Account act){
        this.act = act;
    }
    
    public void run(){
        act.withdraw(1000.0);
        System.out.println("取款1000.0成功,余额:"+act.getBalance());
    }
    
}
//账户
class Account{
    private String actno;
    private double balance;
    
    //Constructor
    public Account(){}
    public Account(String actno,double balance){
        this.actno = actno;
        this.balance = balance;
    }
    //setter and getter
    public void setActno(String actno){
        this.actno = actno;
    }
    public void setBalance(double balance){
        this.balance = balance;
    }
    public String getActno(){
        return actno;
    }
    public double getBalance(){
        return balance;
    }
    
    //对外提供一个取款的方法
    public void withdraw(double money){//对当前账户进行取款操作
        //把需要同步的代码,放到同步语句块中
        /*
        	原理:当线程遇到synchronized关键字,就去找对象锁,找到了就执行同步语句块,执行完后归还对象锁,找不到的话就等待(所有的对象都有一个对象锁)
        */
        synchronized(this){//参数:共享对象
            double after = balance - money;
            //延迟
            try{Thread.sleep(1000)}catch(Exception e){}
            //更新
            this.setBalance(after);
        }
    }
    /*
    //synchronized关键字添加到成员方法上,线程拿走的也是this的对象锁
    public synchronized void withdraw(double money){//对当前账户进行取款操作
        //把需要同步的代码,放到同步语句块中
        /*
        	原理:当线程遇到synchronized关键字,就去找对象锁,找到了就执行同步语句块,执行完后归还对象锁,找不到的话就等待(所有的对象都有一个对象锁)
        */
    */
     /*
            double after = balance - money;
            //延迟
            try{Thread.sleep(1000)}catch(Exception e){}
            //更新
            this.setBalance(after);
        
    }
	*/
}

类锁:如果synchrnoized加在有static关键字的地方就是加了类锁

死锁

image-20200911132941612

/*
	死锁
*/
public class DeadLock{
    public static void main(Stirng[] args){
        
        Object o1 = new Object();
        Object o2 = new Object();
        
        Thread t1 = new Thread(new T1(o1,o2));
        Thread t2 = new Thread(new T2(o1,o2));
        
        t1.start()
        t2.start()
    }
}

class T1 implements Runnable{
    Object o1;
    Object o2;
    
    T1(Object o1,Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }
    public void run(){
        synchronized(o1){
            Thread.sleep(1000);
            synchronized(o2){
                
            }
        }
    }
}

class T2 implements Runnable{
    Object o1;
    Object o2;
    
    T2(Object o1,Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }
    public void run(){
        synchronized(o2){
            Thread.sleep(1000);
            synchronized(o1){
                
            }
        }
    }
}

六、守护线程

从线程分类上可以分为:用户线程和守护线程。守护线程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那守护线程就不会结束,例如java中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的程序结束,它才会结束。

/*
	守护线程
	其他所有用户线程结束,则守护线程退出
	守护线程一般都是无限执行
*/
public class ThreadTest19{
    
    public static void main(Stirng[] args){
        Thread t1 = new Processor();
        t1.setName("t1");
        
        //将t1修改为守护线程
        t1.setDaemon(true);
        t1.start();
        //主线程
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
            Thread.sleep(1000);
        }
    }
    
}
class Processor{
    public void run(){
        int i = 0;
        while(true){
            i++;
            System.out.println(Thread.currentThread().getName()+"-->"+i);
            Thread.sleep(1000);
        }
    }
}

七、定时器的使用

/*
	关于定时器的应用
	作用:每隔一段固定的时间执行一段代码
*/
import java.text.*;
import java.util.*;
public class TimerTest01{
    
    public static void main(String[] args){
        //创建定时器
        Timer t = new Timer();
        //指定定时任务
        t.schedule(new LogTimerTask(),//定时任务
                   new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss").parse("2020-12-12 10:00:00 000"),//开始时间
                  10*1000//间隔
                  );
        			
    }
    
}

//指定定时任务
class LogTimerTask extends TimerTask{
    
    public void run(){
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss").format(new Date()));
    }
}

八、windows的任务计划

在windows中的任务计划程序可以设置定时任务

image-20200911142508669

java.util.*;
public class TimerTest01{

public static void main(String[] args){
    //创建定时器
    Timer t = new Timer();
    //指定定时任务
    t.schedule(new LogTimerTask(),//定时任务
               new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss").parse("2020-12-12 10:00:00 000"),//开始时间
              10*1000//间隔
              );
    			
}

}

//指定定时任务
class LogTimerTask extends TimerTask{

public void run(){
    System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss").format(new Date()));
}

}




## 八、windows的任务计划

在windows中的任务计划程序可以设置定时任务

[外链图片转存中...(img-xSqSpS06-1599805595791)]



参考自:https://www.bilibili.com/video/BV1kx411h7jv?p=223
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Antgeek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值