什么是程序:有序严谨的指令集称为程序。

什么是进程:程序的同时多运行称为进程。

什么是线程:程序中不同的执行路经称为线程,线程是程序的最小执行单位。

多线程:如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称为”多线程”

  • 多线程的好处:

 

    • 充分利用cpu的资源 编程简单
    • 简化编程模型 效率高
    • 带来良好的用户体验 易于资源共享

 

 

新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

 

就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

 

运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

 

阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

 

等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

 

同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

 

其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

 

死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

在Java中创建线程的几种方式

  • 继承Thread类创建线程

//继承Thread类创建线程
public class MyThread extends Thread{
    public void run(){
        for(int i=0;i<100;i++){
    System.out.println(Thread.currentThread().getName());
        }
    }
}
public static void main(String[] args) {
        MyThread mt = new MyThread();
//需使用start()方法启动线程,如果直接调用线程中run()方法会:
        //1.只有主线程一条执行路径2.依次调用了两次run()方法
        mt.start();}

 

  • 实现Runnable接口创建线程

//实现Runnable接口创建线程
public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName());
        }
    }
public static void main(String[] args) {
    
        //创建线程对象
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);
        t.start();
    }

 

  • 实现Callable接口创建Thread线程

public class MyThread implements Callable<Integer> {
    
    @Override
    public Integer call() throws Exception {
System.out.println("子线程:"+Thread.currentThread().getName());
        return null;
    }
}
public class Test {
    public static void main(String[] args) {
        FutureTask<Integer> ft = new FutureTask<Integer>(new MyThread());
        Thread t = new Thread(ft,"01");
        System.out.println("主线程:"+Thread.currentThread().getName());
        t.start();
    }
}

 

  • 通过Future 创建线程
public class CallableThreadTest implements Callable<Integer> {
    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的线程").start();  
            }  
        }  
        try  
        {  
            System.out.println("子线程的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
  
    }
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  
}

 

线程调度方法:

  • setPriority(int newPriority);更改线程的优先级

线程优先级由1~10表示,1最低,默认优先级为5
优先级高的线程获得CPU资源的概率较大

    1. MAX_PRIORITY:静态常量 表示最大优先级
    2. MIN_PRIORITY:静态常量 表示最小优先级
  • sleep(long millis);在指定毫秒数内让当前正在执行的线程休眠

让线程暂时睡眠指定时长,线程进入阻塞状态
睡眠时间过后线程会再进入运行状态
millis为休眠时长,已毫秒为单位
调用sleep()方法需处理InterruptedException异常

  • join(); 等待该线程终止

Join(long millis);
Join(long millis,int nanos);
使当前线程暂停执行,等待其他线程结束后在继续执行本线程
Millis:已毫秒为单位 的等待时长
Nanos:要等待的附加纳秒时长
需处理InterruptedException异常

  • yield(); 暂停当前正在执行的线程对象,并执行其他线程

暂停当前线程,允许其他具有相同优先级的线程获运行机会
该线程处于就绪状态,不转为阻塞状态
只是提供一种可能,但是不能保证一定会实现礼让

  • interrupt(); 中断线程
  • isAlive();测试线程是否处于活动状态

线程的同步:

线程不同步会遇到的问题:(当多个线程共享同一资源时,一个线程未完成全部操作的时候,其他线程会修改数据,造成数据不安全问题。
线程同步)

  • 使用synchronized修饰的方法控制对类成员变量的访问
  • 访问修饰符 synchronized 返回类型 方法名(参数列表){……}
    synchronized 访问修饰符 返回类型 方法名(参数列表){……}

Synchronized()内为需同步的对象,通常为this
效果与同步方法相同
synchronized就是为当前的线程声明一把锁
多个并发线程访问同一资源的同步代码块时
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

package demo01;
//子线程在调用son方法的时候一上来就能获取同步监视器this
//父线程在调用father函数的时候一上来就是获取不到
//字父线程执行的四种情况: 子 父  父 子  子子 父父
//wait:Object (导致线程阻塞,并释放对同步监视器的占用 )
//notify:Object (唤醒线程的作用,提示后续等待该锁的线程,可以进行访问)
public class FatherAndSon {
    boolean flag=true;
    
    public void son(){
        synchronized (this) {
            //子一定是先执行的
            if(!flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 3; i++) {
                System.out.println("儿子线程执行第"+(i+1)+"次");
            }
            flag=false;//1:把机会让给父线程 2、防止子线程再次执行
            this.notify();
        }
        
    }
    public  void father(){
        synchronized (this) {
            //父是后执行的
            if(flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("老子线程执行第"+(i+1)+"次");
            }
            flag=true;//1:把机会让给子线程 2、防止父线程再次执行
            this.notify();
        }
        
    }
}

 

package demo01;

//在Java中什么是所谓的同一资源 static
//只存在一个对象的情况下  ticket 
//多个对象: a.ticket b.ticket c.ticket
//保证一种情况:
//synchronized:方法(整个所修饰的方法体都是线程同步的范围)(this:)====>synchronized(this)
//synchronized:代码块(代码块中的内容是线程同步的范围)
public class Test {
   public static void main(String[] args) throws InterruptedException {
       final FatherAndSon fs=new FatherAndSon();
       
       Thread t1=new Thread(new Runnable(){  
         @Override
         public void run() { //子线程的线程体中
             for (int i = 0; i < 10; i++) {
                  fs.son();
             }    
         } 
       });
       t1.start();
       
      //父线程的线程体中
        for (int i = 0; i < 10; i++) {
          fs.father();
       }
   }  
}

 

线程池:

尽量保证任务数不要超过最大线程数+阻塞队列的长度

  • 使用线程池的好处:
  1. 重用存在的线程,减少对对象创建,消亡的开销
  2. 有效控制最大并发数,提高系统资源使用率
  3. 定时执行,定期执行
  • 线程池所在的包:

Java.util.concurrent
顶级接口Executor,真正的线程池接口时ExecutorService
Java.util.concurrent.Executors类提供创建线程池的方法

自定义线程池:

/**
 * 
 * 创建自定义线程池
 * 
 * */
public class Test {
    public static void main(String[] args) {
        //创建自定义线程池   尽量保证:任务数不要超过最大线程数+阻塞队列的长度
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,7,300,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(4));
        
        for(int i=0;i<12;i++){
            executor.execute(new MyRunnable(i));
            System.out.println("线程池中线程数:"+executor.getPoolSize()+",对列中等待任务执行数:"+executor.getQueue().size()+",已经执行完的任务数:"+executor.getCompletedTaskCount());
        }
        executor.shutdown();
    }
}


class MyRunnable implements Runnable{
    int num;//第几个任务
     
    public MyRunnable(int num) {
        super();
        this.num = num;
    }
    @Override
    public void run() {
       System.out.println("正在执行任务"+num);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("任务"+num+"执行完毕");
    }
    
    
}

 

后台线程的特点:

  1. 随着前台线程的运行而运行
  2. 随着前台线程的消亡而消亡
  3. SetDaemon():将线程设置为后台线程 GC 传入为true是将线程设置为后台线程,默认false
  4. isDaemon():判断是否是后台线程

 

 

posted on 2019-06-16 15:19  走走马观观花  阅读(107)  评论(0编辑  收藏  举报