java 线程基础知识
2015-07-05 21:02
543 查看
JAVA线程基础
每一个Java程序都至少有一个线程--主线程。当一个Java程序启动时,JVM会自动创建主线程,并在该线程中调用程序的main()方法 JVM 还会创建了其他线程,即使我们看不到,如,与垃圾回收,对象终止和其他JVM内务处理相关的线程。 其他工具也创建线程,如AWT(抽象窗口工具箱[Abstract Windowing Toolkit])或Swing UI工具箱,servlet容器,应用程序服务器和RMI(远程方法调用[Remote Method Invocation])。 使用线程的一些原因: 1.使UI响应更快 2.利用多处理器系统 3.简化建模 4.执行异步或后台处理
线程生命
创建线程每个Java程序至少包含一个线程:主线程。其他线程可以通过实例化Thread对象或实例化继承Thread的对象来创建其他线程。
启动线程
Thread线程在调用start()之前,就已经存在,并且退出之后仍然存在。这样可以控制或获取关于以创建的、线程的信息,即使线程还没有启动或已经完成了
结束线程
线程到达其run()方法的末尾
线程抛出一个未捕获到的Exception或Error
另一个线程调用一个弃用的stop()方法。
加入线程
Thread API 包含了等待另一个线程完成的方法:join()方法。当调用Thread.join()时,调用线程将阻塞,直到目标线程完成为止
Thread.join()通常由使用线程的程序使用,以将大问题划分成许多小问题,每个小问题分配一个线程。
调度
永远不要假设一个线程会在另一个线程之前执行某些操作,除非已经使用了同步以强制一个特定的执行顺序。
public class ThreadTest2 { static class Thread1 extends Thread { @Override public void run(){ System.out.println("A"); System.out.println("B"); } } static class Thread2 extends Thread { @Override public void run(){ System.out.println("1"); System.out.println("2"); } } public static void main(String[] args) { System.out.println("启动两个线程!"); Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); thread2.start(); while(thread1.isAlive() || thread2.isAlive()); System.out.println("结束!"); } }
可能的结果
- A12B
- A1B2
- AB12
- 12AB
- 1A2B
- 1AB2
休眠
Thread API 包含了一个sleep()方法,它将使当前线程进入等待状态,直到过了一段指定时间,或者直到另一个对当前线程的Thread对象调用了Thread.interrupt(),从而中断了线程。当过了指定时间后,线程又将变为可运行状态。
Thread.yield()方法就像Thread.sleep()一样,但是它不引起休眠,而是暂停当前线程片刻,这样其他线程可以运行。
守护程序线程
当且仅当创建线程是守护线程时,新线程才是守护程序。
Java程序在它的所有非守护线程完成之后退出。
线程字段
类型 | 字段 | 描述 |
---|---|---|
static int | MAX_PRIORITY | 线程可以具有的最高优先级。 |
static int | MIN_PRIORITY | 线程可以具有的最低优先级。 |
static int | NORM_PRIORITY | 分配给线程的默认优先级。 |
long | id | 线程标识符(getter) |
String | name | 线程名字(setter和getter) |
int | priority | 线程优先级(setter和getter) |
Thread.State | state | 线程状态(getter) |
boolean | alive | 是否处于活动状态(is方法) |
boolean | daemon | 是否为守护线程(setter,is方法) |
boolean | interruptted | 是否被中断(is方法) |
线程方法
构造方法 | 描述 |
---|---|
Thread() | |
Thread(String name) | 定义线程的名字 |
Thread(Runnable target) | 使用Runnale接口创建线程 |
静态方法 | 描述 |
---|---|
currentThread():Thread | 返回对当前正在执行的线程对象的引用。 |
sleep(long millis) | 休眠millis毫秒 |
yield() | 暂停当前正在执行的线程对象,并执行其他线程。 |
interrupted() | 测试当前线程是否已经中断,该方法会清除线程的 中断状态 |
实例方法 | 描述 |
---|---|
start() | 线程开始执行 |
run() | Thread子类重写run(),实现Runnable类要实现该方法 |
join() | 等待线程的执行结束 |
toString() | 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。 |
interrupt() | 中断线程, |
在一个循环中调用sleep(),应该将循环放到try-catch之中,如果将try-catch放到循环中,则即使线程被中断,仍然可能继续执行
线程状态
isAlive():判断线程状态的方法,如果线程处于就绪,阻塞,运行状态,则返回true,如果线程处于新建并且没有启动的状态,或者已经结束,则返回false interrupt():当线程处于就绪状态或运行状态时,给它设置一个中断标志;当线程处于阻塞状态时,它将被唤醒并进入就绪状态,同时抛出异常java.lang.InterruptedException。
共享对数据的访问
存在于一个内存空间的所有线程
线程与同一个进程中的其他线程共享相同的进程上下文,包括内存。只要访问共享变量(静态或实例字段),线程就可以方便的互相交换数据,但线程还必须确保它们以受控的方式访问共享变量,以避免它们互相干扰对方的更改。
受控访问的同步
为了确保可以在线程之间以受控方式访问数据,Java语言提供了两个关键字:synchronized和volatile synchronized:确保了一次只有一个线程可以执行代码的受保护部分(互斥,mutual exclusion 或者说mutex),而且它确保了一个线程更改的数据对于其他线程是可以见的。 volatile,只适合控制对基本变量(整数,布尔变量等)的当个实例的访问。当一个变量被声明成volatile,任何对该变量的写操作都会绕过高速缓存,直接写入主内存,而任何对该变量的读取也都会绕过高速缓存,直接从主内存中取。这表示所有线程在任何时候看到的volatile变量值都相同。
用锁保护原子代码块
同步使用监控器(monitor)或锁的概念,以协调对特定代码块的访问。 每个Java对象都一个相关的锁。同一时间只能有一个线程持有Java锁。当线程进入synchronized代码块时,线程会阻塞并等待,直到锁可用,当它可用时,就会获得这个锁,然后执行代码块。当控制退出受保护的代码块时,即到达了代码块末尾或者抛出了没有在synchronized块中捕获异常时,就会释放锁。 Java锁定合并了一种互斥形式。每次只有一个线程可以持有锁。锁用于保护代码块或整个方法,必须记住是锁的身份保护了代码块,而不是代码块本身,这一点很重要。一个锁可以保护许多代码块或方法。 仅仅因为代码块由锁保护并不表示两个线程不能同时执行该代码块。只表示如果两个线程正在等待相同的锁,则它们不能同时执行该代码。 对于普通的synchronized方法,这个锁是一个对象,将针对它调用方法;对于静态synchronized方法,这个锁是与Class对象相关的监控器,在该对象中声明了方法。 由于同步防止了多个线程同时执行一个代码块,因此性能上就有问题,即使在单处理器系统上,最好在尽可能最小的需要保护的代码块上使用同步。 加锁同步(显示加锁)。一个锁是一个Lock接口的实例,定义了加锁和释放锁的方法。锁也可以使用newCondition()方法来创建任意个数的condition对象用来进行线程通信。 java.util.concurrent.locks.*; 接口:Lock lock() unlock() newCondition():Condition 实现类:ReentrantLock ReentrantLock() ReentrantLock(fair:boolean) 创建具有公平策略的锁 ReentrantLock是为创建相互排斥的锁的Lock的具体实现。公平策略:将锁给等待时间最长的线程。否则随机。 线程间协作,通过保证在临界区上多个线程的互斥,线程同步完全可以避免竞争状态的发生,但是有时候却需要线程间协作(消费者和生产者)。 通过调用Lock对象的newCondition()方法创建条件,然后通过await(),signal()和signalAll来实现线程间通信 java.util.concurrent.*; 类:Condition await() signal() signalAll():Condition
wait()、notify() 和notifyAll() 方法
需要与synchronized一起使用,因此直接学会用synchronized就可以了 Object类还包括一些方法,可以让线程相互通知事件的发生 Object类定义了wait(),notify()和notifyAll()方法,要执行这些方法,必须拥有相关对象的锁。 wait()会让调用线程休眠,直到用Thread.interrupt()中断它,过了指定时间,或者另一个线程用notify()或notifyAll()唤醒它。 当对某个对象调用notify()时,如果有任何线程正在通过wait()等待该对象,那么就会唤醒其中一个线程。当对某个对象调用notifyAll()时,会唤醒所有正在等待该对象的线程。 这些方法时更复杂的锁定,排队,和并发性代码的构件。但是,notify()和notifyAll()的使用很复杂,尤其是,使用notify()来替代notifyAll()是有风险的。除非你确实知道正在做什么,否则就使用notifyAll()。 可以使用util.concurrent包,这是一个被广泛使用的开源工具箱,里面都是有用的并发性实用程序。
public class SynchronizedTest { //将一个对象设置为一把锁。 private static Object lockObject = new Object(); private static int x , y; private static class Thread1 extends Thread{ @Override public void run(){ synchronized(lockObject){ x = y = 0; System.out.println(x); } } } private static class Thread2 extends Thread{ @Override public void run(){ synchronized(lockObject){ x = y = 1; System.out.println(y); } } } public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); thread2.start(); System.out.println("Hello World!"); } }
线程池
管理并发执行任务个数的理想方法 Java提供了Executor接口来执行线程池中的任务,提供ExecutorService接口来管理和控制任务。ExecutorService是Executor的子接口。 java.util.concurrent.*; 类:Executors 静态方法 newFixedThreadPool(numberOfThread):ExecutorService 固定数目的线程池 newCachedThreadPool():ExecutorService 线程池按需创建线程 接口:ExecutorService,继承Executor shutdown() 关闭,不再接受新任务 shutdownNow():List<Runnable> 立即关闭,并结束所有任务,返回未完成任务 isShutdown() isTerminated() 接口:Executor execute(Runnable object)
信号量
信号量可以用来限制访问共享资源的线程数。在访问资源之前,线程必须从信号量获取许可;在访问资源之后,这个线程必须将许可返回给信号量。 java.util.concurrent 类:Semaphore Semaphore(numberOfPermits:int) Semaphore(numberOfPermits:int,fair:boolean) acquire() :获取信号量许可。如果无许可可用,线程就被锁住知道有许可可用 release() :释放一个许可 只有一个许可的信号量可以用来表示互斥的锁。
用例
1、使用一个线程用于计时,并使用另一个线程完成工作实例程序的功能是:使用两个线程,一个用于计时,一个用于执行实际工作,主线程使用非常简单的的算法计算素数;另一个线程用于计时十秒钟。
public class MainThread extends Thread { private static final long MAX_PRIMES = 1000000; private static final int MAX_SECONDS = 10000; private static volatile boolean finished = false; private long primes = 0; @Override public void run(){ boolean flag = true; for(long count = 2; count < MAX_PRIMES; count ++){ if(finished){ break; } //判断是否是素数 for(int i = 0 ; i < Math.sqrt(count) ; i ++){ if(count % i == 0){ flag = false; break; } } if(flag){ primes++; System.out.println("素数个数:" + primes); } flag = true; } } public static void main(String[] args) { MainThread mainThread = new MainThread(); mainThread.start(); try{ Thread.sleep(MAX_SECONDS); }catch(InterruptedException e){ System.out.println("时间到!"); } finished = true; System.out.println("时间到!"); } }
2、用多个线程分解大任务
计算1000000之内的素数个数
public class CountLargePrimes { static class SmallPlan extends Thread { private long start; private long end; private long count; //构造函数 public SmallPlan(long start , long end){ super(); this.start = start; this.end = end; count = 0; } public long getCount(){ return count; } //计算素数个数 private void primes(){ for(long i = start ; i <= end ; i ++){ if(isPrime(i)){ count++; } } } //判断是否是素数 private boolean isPrime(long num){ for(long i = 2; i <= Math.sqrt(num); i++){ if(num % i == 0){ return false; } } return true; } @Override public void run(){ primes(); } } public static void main(String[] args) { System.out.println("开启九个任务!"); long startTime = System.currentTimeMillis(); long count = threadCount(); long endTime = System.currentTimeMillis(); System.out.println("1000000里面素数的个数是:" + count); System.out.println("耗时:" + (endTime - startTime)); } public static long threadCount(){ //78498个,1329ms final long M = 100000; SmallPlan[] threads = new SmallPlan[10]; long start = 2; long end = M; long count = 0; threads[0] = new SmallPlan(start,end); threads[0].start(); for(int i = 1; i <= 9 ; i++){ start = i*M+1; end = (i+1)*M; threads[i] = new SmallPlan(start,end); threads[i].start(); } for(int i = 0 ; i < 10 ; i ++){ //等待十个进程结束,然后汇总结果 try{ threads[i].join(); }catch(InterruptedException ex){ } count += threads[i].getCount(); } return count; } public static long FunCount(){ //78498个,2514ms long count = 0; //计算素数个数 for(long i = 2 ; i <= 1000000; i++){ if(isPrime(i)){ count++; } } //判断是否是素数 return count; } private static boolean isPrime(long num){ for(long i = 2; i <= Math.sqrt(num); i++){ if(num % i == 0){ return false; } } return true; } }
3、生产者和消费者
import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.*; public class ProducerAndConsumer { //创建缓冲区 private static Buffer buffer = new Buffer(); public static void main(String[] args) { System.out.println("Hello World!"); Thread producer = new Thread(new Producer()); Thread consumer = new Thread(new Consumer()); producer.start(); consumer.start(); } //消费者 static class Consumer implements Runnable { public void run(){ try{ while(true){ buffer.poll(); Thread.sleep((int)(Math.random()*1000)); } }catch(InterruptedException ex){} } } //生产者 static class Producer implements Runnable { public void run(){ try{ while(true){ buffer.push(new Random().nextInt(20)); Thread.sleep((int)(Math.random()*1000)); } }catch(InterruptedException ex){} } } } //缓冲池 class Buffer { //创建锁 private static Lock lock = new ReentrantLock(); //条件1---缓冲池空 private static Condition empty = lock.newCondition(); //条件2---缓冲池满 private static Condition full = lock.newCondition(); //缓冲池大小 private static final int size = 10; //缓冲池 private Queue<Integer> queue = new LinkedList<Integer>(); //取数 public void poll(){ lock.lock(); try{ while(queue.size() == 0){ System.out.println("\t\t\t\tWait for producer:"); empty.await(); } int num = queue.poll(); System.out.println("\t\t\t\t["+queue.size()+"]poll:" + num); full.signal(); }catch(InterruptedException ex){ }finally{ lock.unlock(); } } //存数 public void push(int num){ lock.lock(); try{ while(queue.size() == 10){ System.out.println("Wait for consumer:"); full.await(); } queue.offer(num); System.out.println("["+queue.size()+"]push:" + num); empty.signal(); }catch(InterruptedException ex){ }finally{ lock.unlock(); } } }
相关文章推荐
- [LeetCode][Java] Add Two Numbers
- 关于mac版eclipse的配置问题
- javaSE学习笔记之反射
- Eclipse关闭检查
- java 错误:由于文件名不对所造成
- eclipse 远程调试mapreduce
- [LeetCode][Java] Two Sum
- 一种JAVA线程轮训调度算法(转载)
- java基础------抽象类和接口
- Java堆排序(HeapSort)算法实现
- Struts2框架学习(三) 数据处理
- Struts2框架学习(二) Action
- Struts2框架学习(一)
- spring框架学习(六)AOP
- spring框架学习(五)注解
- spring框架学习(四)自动装配
- Eclipse下搭建Corba开发环境+HelloWorld实现过程
- spring框架学习(三)junit单元测试
- java中的内部类和Lambda表达式
- spring框架学习(二)依赖注入