Java基础加强之多线程篇(线程创建与终止互斥通信本地变量)

Posted 学习、改良、极致

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础加强之多线程篇(线程创建与终止互斥通信本地变量)相关的知识,希望对你有一定的参考价值。

线程创建与终止

线程创建

Thread类与Runnable接口的关系

public interface Runnable {
  public abstract void run();
}

public class Thread implements Runnable {
    /* What will be run. */
  private Runnable target;
  ......
  /**
   * Causes this thread to begin execution; the Java Virtual Machine
   * calls the <code>run</code> method of this thread.
   */
  public synchronized void start() {......}

  ......
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
  }
  ......
}

 

Thread类与Runnable接口都位于java.lang包中。从上面我们可以看出,Runnable接口中只定义了run()方法,Thread类实现了Runnable 接口并重写了run()方法。当调用Thread 类的start()方法时,实际上Java虚拟机就去调用Thread 类的run()方法,而Thread 类的run()方法中最终调用的是Runnable类型对象的run()方法

继承Thread并重写run方法

public class ThreadTest1 extends Thread {
    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread 1:" + Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        ThreadTest1 thread = new ThreadTest1 ();
        thread.start();
    }//main end
}

 

可以写成内部类的形式,new Thread(){@Override run(...)}.start();

实现Runnable接口并重写run方法

public class ThreadTest2  implements Runnable {
    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread 3:" + Thread.currentThread().getName());
        }
    }
    public static void main(String[] args) {
        ThreadTest2  thread3 = new ThreadTest2();
        Thread thread = new Thread(thread3);
        thread.start();
    }//main end
}

可以写成内部类的形式,new Thread(new Runnable(){@Override run(...)}).start();

 

线程终止

当调用Thread类的start()方法时,将会创建一个线程,这时刚创建的线程处于就绪状态(可运行状态),并没有运行,处于就绪状态的线程就可以等JVM调度。当JVM调度该线程时,该线程进入运行状态,即执行Thread类的run()方法中的内容。run()方法执行完,线程结束,线程进入死亡状态。这是线程自然终止的过程,我们也可以通过Thread类提供的一些方法来终止线程。

interrupt()\\isInterrupted()\\interrupted()方法介绍

stop()方法没有做任何的清除操作就粗暴终止线程,释放该线程所持有的对象锁(下文将介绍),受该对象锁保护的其它对象对其他线程可见,因此具有不安全性。

 

suspend()方法会使目标线程会停下来,但仍然持有在这之前获得的对象锁,对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。

 

终上所述,不建议使用stop()方法和suspend()方法来终止线程,通常我们通过interrupt()方法来终止处于阻塞状态和运行状态的线程

 

需要注意的是,interrupt()方法不会中断一个正在运行的线程,仅仅是将线程的中断标记设为true,当调用了阻塞方法之后,线程会不断监听中断标志,如果为true,则产生一个InterruptedException异常,将InterruptedException放在catch中就能终止线程。

 

isInterrupted()方法可以返回中断标记,常用循环判断条件。

 

interrupted()方法测试当前线程是否已经中断,线程的中断标志由该方法清除。interrupted()除了返回中断标记之外,它还会清除中断标记

 

interrupt()用法

看下面例子

 

public class ThreadInterruptedTest extends Thread {
    @Override
    public void run() {
            try {
                int i = 0;
                while(!isInterrupted()) {
                    i ++ ;
                    Thread.sleep(1000);
                    System.out.println(this.getName() + " is looping,i=" + i);
                }
            } catch (InterruptedException e) {
                System.out.println(this.getName() + 
                        " catch InterruptedException,state:" + this.getState());  
                e.printStackTrace();
            }
    }

    public static void main(String[] args) throws Exception {
        
        ThreadInterruptedTest thread = new ThreadInterruptedTest();
        System.out.println(thread.getName() 
                + " state:" + thread.getState());  
        
        thread.start();
        System.out.println(thread.getName() 
                + " state:" + thread.getState());  
        
        Thread.sleep(5000);
        
        System.out.println("flag: " + thread.isInterrupted());
        
        //发出中断指令
        thread.interrupt();
        
        System.out.println("flag: " + thread.isInterrupted());
        
        System.out.println(thread.getName() 
                + " state:" + thread.getState());  
        
        System.out.println(thread.interrupted());
    }
}

 

运行结果

 

Thread-0 state:NEW
Thread-0 state:RUNNABLE
Thread-0 is looping,i=1
Thread-0 is looping,i=2
Thread-0 is looping,i=3
Thread-0 is looping,i=4
flag: false
flag: true
Thread-0 state:TIMED_WAITING
Thread-0 catch InterruptedException,state:RUNNABLE
false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.itpsc.thread.ThreadInterruptedTest.run(ThreadInterruptedTest.java:11)

 

从运行结果可以看出,调用interrupt() 发出中断指令前,中断标志位false,发出中断指令后中断标志位为true,而调用interrupted()方法后则中断标志被清除。从发出的异常来看,是在一个sleep interrupted,且发出异常后线程被唤醒,以便线程能从异常中正常退出。

 

线程运行状态图

线程从创建到终止可能会经历各种状态。在java.lang.Thread.State类的源码中,可以看到线程有以下几种状态:NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED。各种状态的转换如下:

 

wps98A1.tmp 

 

当通过Thread t = new Thread()方式创建线程时,线程处于新建状态;当调用t.start()方法时,线程进入可运行状态(注意,还没有运行);处于可运行状态的线程将在适当的时机被CPU资源调度器调度,进入运行状态,也就是线程执行run()方法中的内容;run()方法执行完或者程序异常退出线程进入终止状态。线程从运行状态也有可能进入阻塞状态,如调用wait()方法后进入等待对象锁(下文将介绍),调用sleep()方法后进行入计时等待。

线程互斥

现在我们已经知道线程的创建与终止了。互斥,是指系统中的某些共享资源,一次只允许一个线程访问,当一个线程正在访问该临界资源时,其它线程必须等待。

对象锁

java中,每一个对象有且仅有一个锁,锁也称为对象监视器。通过对象的锁,多个线程之间可以实现对某个方法(临界资源)的互斥访问。那么,如何获取对象的锁呢?当我们调用对象的synchronized修饰的方法或者synchronized修饰的代码块时,锁住的是对象实例,就获取了该对象的锁

全局锁

Java中有实例对象也有类对象,竟然有对象锁,那么久有类锁,也称全局锁当synchronized修饰静态方法或者静态代码块时,锁住的是该类的Class实例(字节码对象),获取的便是该类的全局锁。看下面获取对象锁实现线程互斥的两种方式。

线程互斥的两种方式

先看下面这个没有实现线程互斥的例子。

 

public class SynchronizedTest {

    public static void main(String[] args) {
        new SynchronizedTest().init();
    }
    
    private void init() {
        final Outputer output = new Outputer();
        //线程1打印"hello,i am thread 1"
        new Thread(new Runnable(){
            @Override
            public void run() {
                while(true) {
                     try{
                         Thread.sleep(1000);
                     }catch(InterruptedException e) {
                         e.printStackTrace();
                     }
                     output.output("hello,i am thread 1");
                }    
            }
        }).start();
        
        //线程2打印"hello,i am thread 2"
        new Thread(new Runnable(){
            @Override
            public void run() {
                while(true) {
                     try{
                         Thread.sleep(1000);
                     }catch(InterruptedException e) {
                         e.printStackTrace();
                     }
                     output.output("hello,i am thread 2");
                }
            }
        }).start();
    }
    
    class Outputer {
        public void output(String name) {
            for(int i=0; i<name.length(); i++) {
                System.out.print(name.charAt(i));
            }
            System.out.println();
        }
    }
}

 

运行结果

hello,i am thread 1
hello,i am thread 2
hello,i am hellthread 1
o,i am thread 2
hello,i am thread 2
hello,i am thread 1
hello,i am thread 2
hello,i am threadhel 2lo,i am thread 
1

线程1和线程2同时调用output方法进行输出,从运行结果可以看出,线程之间没有执行完各自的输出任务就被交替了运行了。下面通过对象的锁实现线程1和线程2对output方法的互斥访问。

synchronized修饰方法

使用synchronized 对output方法进行修饰,可以让调用者获得锁。synchronized 修饰方法没有显示声明锁的对象,默认是当前方法所在类的对象this

 

public synchronized void output(String name) {
    for(int i=0; i<name.length(); i++) {
        System.out.print(name.charAt(i));
    }
    System.out.println();
}  

synchronized修饰代码块

使用synchronized 对output方法中的代码块进行修饰,也可以让调用者获得锁。

 

public void output(String name) {
    synchronized(this){
        for(int i=0; i<name.length(); i++) {
            System.out.print(name.charAt(i));
        }
        System.out.println();
    }
} 

使用synchronized之后,线程1和线程2output方法实现了互斥访问。

hello,i am thread 1
hello,i am thread 2
hello,i am thread 1
hello,i am thread 2
hello,i am thread 1
hello,i am thread 2
hello,i am thread 1

synchronized用法

先看下面的例子,我们来总结下synchronized的一些常用用法。

 

public class SynchronizedTest {

    public static void main(String[] args) {
        new SynchronizedTest().init();
    }
    
    private void init() {
        final Outputer output = new Outputer();
        //线程1打印"hello,i am thread 1"
        new Thread(new Runnable(){
            @Override
            public void run() {
                output.output("hello,i am thread 1");
            }
        }).start();
        
        //线程2打印"hello,i am thread 2"
        new Thread(new Runnable(){
            @Override
            public void run() {
                output.output("hello,i am thread 2");
            }
        }).start();
    }
    
    static class Outputer {
        public synchronized void output(String name) {
            for(int i=0; i<5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name);
            }
        }
        
        public void output2(String name) {
            synchronized(this) {
                for(int i=0; i<5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(name);
                }
            }
        }
        
        public void output3(String name) {
            for(int i=0; i<5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name);
            }
        }
        
        public static synchronized void output4(String name) {
            for(int i=0; i<5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name);
            }
        }
        
        public void output5(String name) {
            synchronized(Outputer.class) {
                for(int i=0; i<5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(name);
                }
            }
        }
    }
}

运行结果

hello,i am thread 1
hello,i am thread 1
hello,i am thread 1
hello,i am thread 1
hello,i am thread 1
hello,i am thread 2
hello,i am thread 2
hello,i am thread 2
hello,i am thread 2
hello,i am thread 2

 

基础篇之多线程总结

Python之多线程:线程互斥与线程同步

java基础之多线程

boost库之多线程间通信

并发编程之多线程篇之四

Java Socket 通信之多线程