1. 进程和线程

  进程是一个应用程序,每个进程都有自己的独立内存空间,进程不依赖线程而独立存在,一个进程可以启动多个线程。

  线程是进程的执行单元,一个进程可以运行多个线程。

  在Dos中输入java HelloWorld后,程序的执行过程:

  会先启动Jvm,Jvm就是一个进程,Jvm在启动一个主线程执行main方法,在启动一个垃圾回收线程负责看护,回收垃圾,至少有两个线程并发运行,一个垃圾回收线程,一个执行main方法的线程。

  在Java中,线程A和线程B,堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈;所以main方法结束了,只代表主线程结束了,其他线程可能还在执行。

 

2. 线程的生命周期

Java 多线程系列01_i++

 

 

  • 新建状态

    线程创建后,就进入新建状态。例如:Thread thread = new Thread();。

  • 就绪状态:   

    当线程对象调用了start()方法后,线程就进入了就绪状态,等待CPU调度。

  • 运行状态:  

    线程获取CPU资源,就可以运行,就处于运行状态。

  • 阻塞状态:  

    线程因为其他原因失去CPU资源暂时停止运行。直到线程进入就绪状态,才可以重新进入运行状态。阻塞状态有三种:

   (1)等待阻塞:线程执行wait()方法,就进入等待阻塞状态。

   (2)同步阻塞:线程获取synchronized同步锁失败(同步锁被其他线程占用中)

   (2)其他阻塞:通过调用sleep()或join()发出I/O请求时,进入阻塞状态。

  • 死亡状态:  

    线程执行完成或被其他任务强制终止

 

3. 实现线程的两种方式

  通过继承Thread创建线程

public class ThreadTest01{
    public static void main(String[] args){
        MyThread thread = new MyThread();
        thread.start();
        
        for(int i=0; i<1000; i++){
            System.out.println("主线程----" + i);
        }
    }
}

class MyThread extends Thread{
    public void run(){
        for(int i=0; i<1000; i++){
            System.out.println("分支线程----" + i);
        }
    }
}

  结果说明:

    如果单纯调用MyThread.run(),那么只是在main()方法上压栈,没有创建分支线程;

    先调用start()方法开辟一个分支栈空间,然后会自动调用run(),在分支栈中和主栈中并发同时输出。

 

  实现Runnable接口创建线程

public class ThreadTest02{
    public static void main(String[] args){
        Thread thread = new Thread(new MyRunnable());    //创建一个可运行对象,创建接口,把可运行对象封装成线程对象
        thread.start();
        
        for(int i=0; i<1000; i++){
            System.out.println("主线程----" + i);
        }
    }
}

class MyRunnable implements Runnable{
    public void run(){
        for(int i = 0; i<1000; i++){
            System.out.println("分支线程----" + i);
        }
    }
}

 

4. 线程方法

  (1)getName()  获取线程名,默认线程名Thread-0;

  (2)setName()  设置线程名;

  (3)Thread.currentThread()  获取当前线程名,返回Thread,在哪个线程中就获取哪个线程名

public class ThreadTest03{
    public static void main(String[] args){
        Thread currentThread = Thread.currentThread();
        System.out.println("当前线程:" + currentThread.getName());
        
        MyThread mt = new MyThread();
        mt.setName("tt");
        System.out.println(mt.getName());
        mt.start();
    }
}

class MyThread extends Thread{
    public void run(){
        for(int i = 0; i<5; i++){
            Thread currentThread = Thread.currentThread();
            System.out.println(currentThread.getName() + "分支线程:" + i);
        }
    }
}

 

  Thread.sleep(long mills)  让当前线程等待mills(毫秒)

public class ThreadTest04{
    public static void main(String[] args){
        for(int i=0; i<5; i++){
            System.out.println(Thread.currentThread().getName() + "----" + i);
            try{
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

 

  interrupt()  线程中断

public class ThreadTest05{
    public static void main(String[] args){
        Thread t = new Thread(new MyRunnable());
        t.setName("tt");
        t.start();
        
        try{
            Thread.sleep(5000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        
        t.interrupt();
    }
}

class MyRunnable implements Runnable{
    public void run(){
        System.out.println(Thread.currentThread().getName() + "---start");
        
        try{
            Thread.sleep(1000*60*60*24*365);  //父类中没有抛出异常,子类中只能try...catch...捕获异常
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        
        System.out.println(Thread.currentThread().getName() + "---end");
    }
}

Java 多线程系列01_阻塞状态_02 

  当线程被wait,sleep,join等阻塞时,调用该线程的interrupt()方法,那么该线程将抛出一个InterruptedException异常(该线程必须提前处理此异常),从而提早的终结被阻塞状态。如果线程没有被阻塞,这是调用interrupt()将不起作用。线程被唤醒的机制也是异常处理机制,当有异常发生时,就可以唤醒线程。

  

  stop()  终止线程

public class ThreadTest06{
    public static void main(String[] args){
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);
        t.setName("tt");
        t.start();
        
        try{
            Thread.sleep(5000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        
        //t.stop();
        mr.flag = false;
    }
}

class MyRunnable implements Runnable{
    boolean flag = true;
    public void run(){
        for(int i=0; i<10; i++){
            if(flag){
                System.out.println(Thread.currentThread().getName() + "---" + i);
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }else{
                return;
            }
        }
    }
}

  stop() 终止线程因为安全性问题,现在已经不推荐使用。

 

5. 线程调度

  getPriority()  获取线程优先级

  setPriority()  设置线程优先级

public class ThreadTest07{
    public static void main(String[] args){
    /*    System.out.println(Thread.currentThread().getName() + "优先级:" + Thread.currentThread().getPriority());
        ThreadTest t = new ThreadTest();
        t.setName("tt");
        t.start();
        System.out.println("线程的最高优先级:"+Thread.MAX_PRIORITY); //10
        System.out.println("线程的最低优先级:"+Thread.MIN_PRIORITY); //1
        System.out.println("线程的正常优先级:"+Thread.NORM_PRIORITY); //5  */
        
        ThreadTest tt = new ThreadTest();
        tt.setName("tt");
        tt.setPriority(10);
        tt.start();
        for(int i=0; i<1000; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
        
    }
}

class ThreadTest extends Thread{
    public void run(){
        //System.out.println(Thread.currentThread().getName() + "优先级:" + Thread.currentThread().getPriority());
        
        for(int i=0; i<1000; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
        
        
    }
}

  线程优先级的范围是1-10,默认优先级是5。

  高优先级线程被分配CPU的概率高于低优先级线程,但是无论级别相同还是不同,线程调度都不对绝对按照优先级执行,根据时间片轮询调度,并发执行。

 

  yield()  线程让位,让线程暂停回到就绪状态

public class ThreadTest08{
    public static void main(String[] args){
        Thread th = new Thread(new yieldTest());
        th.setName("tt");
        th.start();
        
        for(int i=0; i<10000; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

class yieldTest implements Runnable{
    public void run(){
        for(int i=0; i<10000; i++){
            if(i % 100 == 0){    
                Thread.yield();    //每过100,让位给主线程
            }
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
        
    }
}

  yield()作用时让步,让当前线程由运行状态进入到就绪状态,从而让其他具有相同优先级的线程获取执行权;但是并不能保证当前线程调用yield后,其他线程线程就一定能获取执行权,也有可能当前线程又继续进入运行状态。

 

  join() 线程合并,让主线程等待子线程执行完后再继续执行主线程。

public class ThreadTest09{
    public static void main(String[] args){
        System.out.println("main begin");
        Thread th = new Thread(new joinTest());
        th.setName("tt");
        th.start();
        try{
            th.join();   //加入到主线程中,执行完后,再执行其他线程
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("main over");
    }
}

class joinTest implements Runnable{
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

 

6. 守护线程  

  setDaemon() 守护线程,一般是死循环,只要主线程结束,守护线程就自动结束

public class ThreadTest10{
    public static void main(String[] args){
        Thread th = new Thread(new MyThread());
        th.setName("备份数据线程");
        th.setDaemon(true);    //备份数据线程设置为用户线程的守护线程,用户线程结束,备份数据线程结束
        th.start();
        
        //用户线程
        for(int i=0; i<10; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

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

 

7. 定时器  

  定时器,间隔一定时间,执行指定程序,java.util.Timer定时器

public class ThreadTest11{
    public static void main(String[] args) throws ParseException{
        Timer timer = new Timer();  //创建定时器
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date date = sdf.parse("2021-03-21 21:18:20");
        timer.schedule(new LogTimer(),date,1000*10);    //每隔10s完成次数据备份
    }
}

class LogTimer extends TimerTask{
    public void run(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String nowtime = sdf.format(new Date());
        System.out.println(nowtime + "完成了一次数据备份");
    }
}

 

8. 实现线程的第三种方式

  实现callable接口,可以获取线程的返回值;

public class ThreadTest12{
    public static void main(String[] args) throws ExecutionException, InterruptedException{
        FutureTask futuretask = new FutureTask(new Callable(){
            public Object call() throws Exception{
                System.out.println("method begin");
                Thread.sleep(1000*5);
                System.out.println("method over");
                int a = 100;
                int b =200;
                return a + b;
            }
        });
        
        Thread th = new Thread(futuretask);
        th.setName("tt");
        th.start();
        
        Object obj = futuretask.get();   //get()方法执行会使主线程进入阻塞状态,主线程必须等待子线程执行完成返回结果
        System.out.println("获取返回值: "+ obj);
    }
}

  这种方式创建线程:

  优点:可以获取线程的返回值;

  缺点:必须等待子线程执行完得到返回结果后,才能执行主线程。