java多线程-基础

1. 线程及进程

进程:进程是系统中正在运行的一个程序,程序一旦运行就是进程。
进程能够当作程序执行的一个实例。进程是系统资源分配的独立实体,每一个进程都拥有独立的地址空间。一个进程没法访问另外一个进程的变量和数据结构,若是想让一个进程访问另外一个进程的资源,须要使用进程间通讯。进程是系统进行资源分配和调度的一个独立单位。
线程:线程能够理解为进程中独立运行的子任务。java

2. 多线程中经常使用方法

2.1 建立线程

经过继承Thread类:
继承Thread方法,重写Thread的run()方法安全

public class MyThread extends Thread{
 
	 @Override
	public void run() {
	    doSomething();
	}
 
	private void doSomething() {
            System.out.println("我是一个线程中的方法");
	}
}

public class NewThread {
	public static void main(String[] args) {
		MyThread myThread=new MyThread();
		myThread.start();//开启一个线程方法
	}
}

复制代码

Thread类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象run方法。若是调用myThread.run()就不是异步执行,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法。
实现Runable接口bash

public class RunnableThread implements Runnable{
 
	@Override
	public void run() {
		doSomeThing();
	}
	private void doSomeThing() {
		System.out.println("我是一个线程方法");
	}
}
复制代码

实现Runable接口类中没有start()方法,须要用Thread构造个方法开启线程。数据结构

public class NewThread {
	public static void main(String[] args) {
		Runnable runnable=new RunnableThread();
		Thread thread=new Thread(runnable);
		thread.start();//开启一个线程方法
	}
}
复制代码

实现Callable接口和Future建立线程
首先建立Callable接口的实现类CallableThread,实现call()方法,而且有返回值。Callable接口是一个带泛型的接口,泛型的类型就是线程返回值的类型。实现Callable接口中的call()方法,方法的返回类型与泛型的类型相同。多线程

public class CallableThread implements Callable<String>{
 
	@Override
	public String call() throws Exception {
		doSomeThing();
		return "须要返回的值";
	}
 
	private void doSomeThing() {
		System.out.println("我是线程中的方法");
	}
}
复制代码

Callable不能直接获取返回值,须要用FutureTask在外部封装一下再获取返回值。并发

public class NewThread {
	public static void main(String[] args) {
		Callable<String> callable=new CallableThread();
		FutureTask<String> futureTask=new FutureTask<String>(callable);
		Thread thread=new Thread(futureTask);
		thread.start();//开启一个线程方法
	       //如下的方法可与上边的线程并发执行
		doSomething();
		try {
			futureTask.get();//获取线程返回值
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
 
	private static void doSomething() {
	}
}
复制代码

三种建立方式的优缺点:异步

  1. 继承Thread显然有个很大的缺点,java是单继承了,若是继承了Thread那么就没法继承其余类。可是继承Thread编写简单,实现方便。
  2. 实现Runnable接口和Callable接口。大体同样,区别就是Callable接口的实现能够有返回值,且能够抛出显示异常。其他大体同样。 他们的优点是实现接口,那么实现类能够有其余父类,避免的Thread的问题,其次能够用一个实现了该接口的对象来创 建多个线程,从而方便一些基本的资源共享,由于是同一个对象。

2.2 currentThread()方法

currentThread()方法可返回代码段正在被那个线程调用的信息。socket

public class MyRun extends Thread {

    public MyRun(){
        System.out.println("构造 thread :" + Thread.currentThread().getName());
        System.out.println("构造 thread :" + this.getName());
    }
    
    @Override
    public void run() {
        System.out.println("Thread.currentThread().getName(): " + Thread.currentThread().getName());
        System.out.println("this.getname:"+ this.getName());
    }
}
复制代码
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        MyRun myRun = new MyRun();
        myRun.setName("aaaa");
        myRun.start();
    }
}
复制代码

运行结果ide

构造 thread :main
构造 thread :Thread-0
Thread.currentThread().getName(): aaaa
this.getnam:eaaaa
复制代码

2.3 isAlive()方法

isAlive()方法用于判断当前线程是否处于活动状态。活动状态时线程已经启动还没有终止。线程处于正在运行或者准备开始运行的状态就认为线程是存活的。函数

2.4 sleep()方法

sleep()方法是在指定的毫秒数内让当前“正在执行的线程”休眠(暂时执行)。这个正在执行的线程是指this.currentThread()返回的线程。

2.5 getId()方法

getId()方法能够取得线程的惟一标识。

2.6 判断线程是否中止

Thread.java类中提供了两种方法来判断线程是否中止。

  1. this.interrupted():测试当前线程是否已经中断,当前线程指运行this.interrupted()的线程。同是具备清除状态的功能,及若是连续两次调用该方法,则第二次调用将返回false。
  2. this.isInterrupted():测试线程是否已经中断。不具有清除状态功能。

2.7 yield()方法

yield()方法的做用是放弃当前的CPU资源,让给其余的任务去占用CPU执行的时间。可是放弃的时间不肯定,有可能刚放弃,立刻又获取了CPU的时间片。

2.8 中止线程

中止线程意味着在线程处理完任务以前停掉正在作的操做,也就是放弃当前操做。
有三种中止线程的方式:
中止一个线程可使用Thread.stop()方法,但最好不要使用,虽然它确实能够中止一个正在运行的线程,可是这个方法是不安全的,并且已经被废弃。 大多数中止线程的操做使用Thread.interrupt()方法,但这个方法不会终止一个正在运行的线程,还须要加入一个判断才能够完成线程的中止。

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
  2. 使用stop方法强行终止线程(这个方法不推荐使用,由于stop和suspend、resume同样,也可能发生不可预料的结果)。
  3. 使用interrupt方法中断线程。

使用退出标志退出线程:
当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其余的须要循环处理的任务。在这种状况下,通常是将这些任务放在一个循环中,如while循环。若是想让循环永远运行下去,可使用while(true){……}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并经过设置这个标志为true或false来控制while循环是否退出。

package chapter2;  
   
public class ThreadFlag extends Thread  {  
    public volatile boolean exit = false;  
   
    public void run(){  
        while (!exit){
             //do something
        };  
    }  
    public static void main(String[] args) throws Exception{  
        ThreadFlag thread = new ThreadFlag();  
        thread.start();  
        sleep(5000); // 主线程延迟5秒  
        thread.exit = true;  // 终止线程thread  
        thread.join();  
        System.out.println("线程退出!");  
    }  
}  
复制代码

使用stop方法终止线程:
使用stop方法能够强行终止正在运行或挂起的线程。咱们可使用以下的代码来终止线程:

thread.stop();  
复制代码

虽然使用上面的代码能够终止线程,但使用stop方法是很危险的,就象忽然关闭计算机电源,而不是按正常程序关机同样,可能会产生不可预料的结果,所以,并不推荐使用stop方法来终止线程。
使用interrupt方法终止线程:
使用interrupt方法来终端线程可分为两种状况:

  1. 线程处于阻塞状态,如使用了sleep,同步锁的wait,socket中的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptException异常。阻塞中的那个方法抛出这个异常,经过代码捕获该异常,而后break跳出循环状态,从而让咱们有机会结束这个线程的执行。一般不少人认为只要调用interrupt方法线程就会结束,其实是错的, 必定要先捕获InterruptedException异常以后经过break来跳出循环,才能正常结束run方法。
public class ThreadSafe extends Thread {
    public void run() { 
        while (true){
            try{
                    Thread.sleep(5*1000);//阻塞5妙
                }catch(InterruptedException e){
                    e.printStackTrace();
                    break;//捕获到异常以后,执行break跳出循环。
                }
        }
    } 

复制代码
  1. 使用while(!isInterrupted()){……}来判断线程是否被中断。
public class ThreadSafe extends Thread {
    public void run() { 
        while (!isInterrupted()){
            //do something, but no throw InterruptedException
        }
    } 
}
复制代码

为何要区分进入阻塞状态和和非阻塞状态两种状况了,是由于当阻塞状态时,若是有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完以后会复位中断状态为false,因此异常抛出以后经过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,所以在线程未进入阻塞的代码段时是能够经过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要经过捕获异常来退出循环。所以使用interrupt()来退出线程的最好的方式应该是两种状况都要考虑。

2.9 暂停线程

暂停线程意味着此线程还能够恢复运行,可使用suspend()方法暂停线程,使用resume()方法恢复线程。

public class MyRun extends Thread {
    private long i = 0;
    public long getI(){
        return i;
    }
    public void setI(long i){
        this.i = i;
    }
    @Override
    public void run() {
        while (true){
            i++;
        }
    }
}
复制代码
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        MyRun myRun = new MyRun();
        myRun.start();
        Thread.sleep(3000);
        myRun.suspend();
        System.out.println("A= " + System.currentTimeMillis() + " i = " + myRun.getI());
//        myRun.resume();
        Thread.sleep(5000);
//        myRun.suspend();
        System.out.println("B= " + System.currentTimeMillis() + " i = " + myRun.getI());
    }
}
复制代码

suspend和resume方法能够是线程暂停和重启,可是若是使用不当,极易形成公共的同步对象的独占和由于线程暂停而致使的数据不一样步的状况。

2.10 守护线程

在java线程中有两种线程,一种是用户线程,一种是守护线程。
守护线程是一种特殊得线程,它得特性有“陪伴”得含义,当进程中不存在非守护线程,则守护线程自动销毁。典型得守护线程就是垃圾回收线程。
可使用setDaemon()设置线程为守护线程。不能把一个正在运行得线程设置为守护线程,因此,setDaemon()方法必须在start()方法前面。而且守护线程中产生得线程也是守护线程。

相关文章
相关标签/搜索