Java 语言 - 高级部分

一、Java多线程

多任务与多线程

多任务(multitasking)

多任务,是指操作系统,在同一时刻运行多个程序的能力。
例如:在浏览网页的同时,可以打印文件。现代操作系统,多核CPU的计算机,并发数不受CPU数目的限制。操作系统将CPU的时间片资源,分配给每个进程,给人并行处理的感觉。

多线程

多线程,在较低层次扩展了多任务的该拿:同一程序执行多个任务。通常每个任务称为一个线程(thread),它是线程控制的简称。可以同时运行一个以上线程的程序称为多线程程序

多线程与多进程的区别

本质区别在于,每个进程都拥有自己的一整套变量,而线程则共享数据。多线程程序,与进程相比更轻量,创建、销毁一个线程比启动新进程的开销小。同时,共享变量使得线程之间通信比进程之间的通信更高效、更容易。

如何创建Java线程

方法一:实现java.lang.Runnable接口

  • 1、定义类。定义一个实现了 Runnable 接口的类,将任务代码转移到该类的run方法中。java.lang.Runnable接口非常简单,仅有一个方法:
package java.lang;

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

//可以按照以下方法实现类
public class MyRunnable implements Runnable{
	public void run(){
		//task code
	}
}
  • 2、创建一个类对象
Runnable r = new MyRunnable();
  • 3、由Runnable创建一个Thread对象:
Thread t = new Thread
  • 4、启动线程:
t.start();
/**
*The Runner Class implements Runnable Interface
/
class Runner implements Runnable{
  public void run(){
    for(int index=0;index<100;index++){

      LogUtil.log("run Runner,Cycle: " + index);
      try{
        if(Thread.currentThread().isInterrupted()){
          LogUtil.log("exit Runner thread.... " );

          break;
        }
        Thread.sleep(7);
      }
      catch(InterruptedException e){
        //e.printStack();
        LogUtil.log("exit Runner thread: " + index);
        Thread.currentThread().interrupt();
        //break;
      }
    }
  }
}

class LogUtil{
  private static String Version = "[Version: V2.1.1-20161214]";
  public static void log(String msg){
    System.out.println(msg);
  }

  public static void printVersion(){
    System.out.println("---------------");
    System.out.println(Version);
    System.out.println("---------------");
  }
}

public class App{
  public static void main(String[] args) {

    LogUtil.printVersion();
    //创建Thread
    Thread t = new Thread(new Runner());
    t.start();

    //sleep main thread
    try{
      Thread.sleep(18);
    }
    catch(InterruptedException ignored){

    }


    t.interrupt();
  }
}

方法二:继 承java .lang.Thread的子类,重载Thread.run()方法

//1、定义Thread的子类,重载run()方法
public class PrimeThread extends Thread{
    public PrimeThread(String name){
        super(name);
    }
 
   public void run(){
     int index = 0;
     try{
          while(index<100){
             System.out.println(String.format("[ %s ]index = %s",this, index++));
                Thread.sleep(1000);
           }
        }catch(Exception ex){

        }
    }
}

//2、创建子类对象
PrimeThread p = new PrimeThread("Comsumer Thread");
//3、调用start()方法(新建线程,执行run()方法)
p.start();


这种方法已经不再推荐。应该从运行机制上减少需要并行运行的任务数量。如果存在多个任务,需要为每个任务创建一个独立线程所付出的代价太大。后续使用线程池解决。

####API java.lang.Thread 1.0
#####void Thread(Runnable target)
构造一个新线程,用于调用给定target任务的run()方法
#####void start()
启动新线程,将引发调用run()方法。这个方法将立即返回,同时新线程将并行运行。
#####void run()
调用关联Runnable的run方法。

方法三:线程池

#线程状态以及属性
###中断线程
当对一个线程调用interrupt方法时,线程的***中断状态***将被置位。这个是每个线程都具备的boolean标志。每个线程都会不时地检查这个标志,以判断线程是否被中断。
####如何检测线程的中断状态是否置位?
如下,首先调用静态的Thread.currentThread方法获取当前线程,然后调用isInterrupted method方法。

while(!Thread.currentThread().isInterrupted()&& more work to do){
	do more work
}

当线程处于阻塞状态(sleep或者wait)时,调用interrupt方法,阻塞调用会被InterruptedException异常中断。(存在不能被中断的阻塞I/O调用,应该考虑选择可中断的阻塞I/O调用)。
没有任何语言方面的需求要求一个被中断的线程必须终止。被中断的线程可以决定如何响应中断请求。它可以被终止,也可以在处理完InterruptedException后继续执行,而不理会中断。但是通常线程会简单地将中断作为一个终止的请求,这种线程的run方法如下:

public void run(){
	try{
		....
		while(Thread.currentThread().isInterrupted()&&more work to do){
			do more work
		}
	}catch(InterruptedException e){
		// thread was interrupted during sleep or wait
	}finally{
		cleanup, if needed
	}
	// exit the run method terminates the thread
	
}

如果在每次迭代之后都调用sleep方法(或者其他可中断方法),则无需调用isInterrupted检测。如下:

public void run(){
	try{
		....
		while(Thread.currentThread().isInterrupted()&&more work to do){
			do more work
			Thread.sleep(delay);
		}
	}catch(InterruptedException e){
		// thread was interrupted during sleep or wait
	}finally{
		cleanup, if needed
	}
	// exit the run method terminates the thread
	
}
API java.lang.Thread 1.0

static Thread currentThread()
返回代表当前执行线程的Thread对象。

void interrupt()

向线程发送中断请求。线程的中断状态将被设置称为true。如果目前该线程被一个sleep调用阻塞。那么,InterruptedException异常被抛出。

bool isInterrupted()

测试线程是否被终止。这个调用buhl该把线程的中断状态。

static boolean interrupted()

测试当前线程(执行此interrypted()命令的线程)是否被中断。注意,这是一个静态方法。同时,这个调用会将当前线程的中断状态重置为false。

线程状态

|序号|状态|描述|
|—|
|1|New(新创建)|当使用new操作符创建一个新线程时,如new Thread®,线程还没有开始运行
|2|Runnable(可运行)|一旦调用start方法,线程处于runnable状态。一个可运行的线程,可能正在运行可能没有运行,取决于操作系统给线程提供运行的时间。(Java规范没有将正在运行,作为一个单独状态)
|3|Blocked(阻塞状态)|当一个线程试图获取一个内部的对象锁(而不是java.util.concurrent库中的锁),而该锁被其他线程持有,则该线程进入阻塞状态。当其他线程释放该锁,同时线程调度器允许本线程持有它时,该线程进入非阻塞状态
|4|Waiting(等待)|当线程等待另一个线程通知调度器一个条件时,自己进入等待状态。在调用Object.wait或者Thread.join方法,或者是java.util.concurrent库钟的Lock或者Condition的等待方法时,就会进入等待状态
|5|Timed waiting(计时等待)|调用了包含超时参数的方法,会导致线程进入Time waiting状态,一直保持到超时期满或者接收到适当的通知。包括:Thread.sleep、 Object.wait、Thread.join、Lock.tryLock、 Condition.wait的计时版本。
|6|Terminated(被终止)|情形一:因为run方法退出而正常死亡。情形二:因为一个没有捕捉的异常终止了run方法而意外死亡的。

API java.lang.Thread 1.0
void join()

等待终止指定的线程。

void join(long millis)

等待指定的线程死亡或者经过指定的毫秒数

Thread.State getState()

得到线程的状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED之一。

线程属性

线程优先级

在Java语言中,每个线程都有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以通过setPriority方法提供或者降低任何一个线程的优先级。可以将优先级设置为在MIN_PRIORITY(在Thread类中定义为1)与MAX_PRIORITY(定义为10)之间的任何值。NORM_PRIORITY被定义为5。
每当线程调度器有机会选择新线程时,它首先选择具有高优先级的线程。但是线程优先级是高度依赖于系统的。当虚拟机依赖于宿主机平台的线程实现机制时,Java线程的优先级被映射到宿主机平台的优先级上,优先级个数可能变多,可能变少。

多线程程序,千万不能将程序构建为功能的正确性依赖于线程的优先级。
####守护线程
守护线程是为其他线程提供服务的。当只剩下守护线程时,虚拟机就会退出了。
调用t.setDaemon(true);可以将线程转行为守护线程。这个方法必须在线程启动之前调用。
####未捕获异常处理器
线程的run方法抛出任何不能被捕获的异常时,线程就会被终止。同时,在线程死亡之前,异常会被传递给一个用于未捕获异常的处理器。该处理器,是一个实现了Thread.UncaughtExceptionHandler接口的类。这个接口仅有一个方法:

void uncaughtException(Thread t,Throwable e);

可以使用Thread.setUncaughtExceptionHandler(UncaughtExceptionHandler eh)方法为任何线程安装一个处理器。也可以static的方法setDefaultUncaughtExceptionHandler为所有线程安装一个默认的处理器。
如果不安装处理器,则默认处理器是空。但是,如果不安装独立的线程处理器,此时的处理器就是线程的ThreadGroup对象。

线程同步与通信

线程通信主要通过共享访问字段或者字段引用的对象完成的,但是有可能出现两种错误:线程干扰(thread interference)和内存一致性错误(memory consistency)。用来防止这些错误的工具是同步(synchronization)。

Java的Memory Model

The Java Memory Model describes what behaviors are legal in multi-threaded code, and how threads may interact through memory.

synchronized 关键字

在Java多线程程序中,不同线程会同时请求访问(读或者写)相同的变量或者资源时(称为竞争资源),正确地使用 synchronized ,就可以避免多线程交互(英文:interleave)地访问竞争资源,实现内存一致性与线程安全。

Deeper

  1. synchronized 使用特定的lock object作为参数(称为锁对象),紧跟着的是**{ 代码块 }**(又称临界区段)。

  2. synchronized 可以修饰对象方法类方法{ 代码块 }

//情况一:代码块
//例如:java.util.Vector   1365行
synchronized(lock object){
   ...
}

//情况二:对象方法
public synchronized  void trimToSize(){
    ...
}

//等同于
public  void trimToSize(){
 synchronized(this){
    ...
 }
}
//情况三:类方法
public synchronized static  void trimToSize(){
    ...
}

//等同于
public  static void trimToSize(){
 synchronized(this.getClass()){
    ...
 }
}

当多线程程序运行时,
(1)如果相互关联的多线程,在执行过程中遇到相同lock object修饰的synchronized时;
(2)他们都会请求lock/acquire/own锁对象,最终会有一个线程获得lock object,继续执行,其他线程都会挂起(处于Wait状态);
(3)此线程执行结束时,会释放lock object。其他挂起线程中,会有一个线程lock/acquire/own这个lock object,被唤醒,继续执行,如此循环(2)、(3)。
(4)任何一个处于**{ 代码块 }的写操作,对于其他关联线程(使用了相同lock object修饰的{ 代码块 }**)都是可见的。

参考

Java 多线程之InnterrupedException

stackoverflow: what-does-synchronized-mean

Java Memory Model

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值