首页 > 代码库 > Java线程初探
Java线程初探
首先复习一下什么是线程和进程。应用程序被加载到内存中并准备运行时,我们就说创建了一个进程。进程是一个具有独立功能的程序关于某个数据集合上一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
而线程是进程的一个实体,是CPU调度和分派的基本单位,是进程更小的能独立运行的基本单位。线程没有自己的系统资源,一个进程的多个线程共享操作系统分配各进程的资源。
一个线程和一创建和撤销另外一个线程,同一个进程中的多个线程之间可以并发的执行。一个可运行的程序至少有一个进程,一个进程至少有一个线程。从逻辑角度上来讲,多线程的意义在于一个应用程序中的多个执行部分可以同时执行。但系统并没有将多个线程看成独立的应用从而给其分配资源,系统资源分配和调度的基本单位还是线程。这就是进程和线程的重要区别。
线程状态
在操作系统中,线程通常有四种状态:wait、blocked、running和dead。OS是以队列的形式维护线程的,新建立的线程会被添加到队列的末端,然后从队列头部获取线程,线程的CPU时间片结束后,该线程被归还到队列的队尾。
守护线程和非守护线程
Java中有两种类型的线程:1、守护线程;2、非守护(用户)线程。他们的区别是,父线程创建的守护线程在父线程结束时,守护线程会自动结束;而父线程创建的非守护线程在父线程结束的时候还会继续存活。而对于进程来说,只要有线程还活着,那么这个进程也还活着。
每一个Java应用程序至少都会有一个非守护线程,即主线程。用户关闭了应用程序时,主线程会死亡。但如果程序创建了其他的非守护线程,主进程会继续存活。
创建线程
在Java程序中要创建线程,可通过以下方式来实现:1、implements Runnable接口;2、extends Thread类;3、通过ThreadGroup类实现。
class ThreadA implements Runnable{ ...... @Override public void run(){ ..... }}class ThreadB extends Thread{ ....... @Override public void run(){ .... }}public class ThreadSample{ public static void main(String[] args){ Thread sampleA = new Thread(new ThreadA()); sampleA.start(); Thread sampleB = new ThreadB(); sampleB.start(); }}
以上是最常用的两种创建线程的方式,在run()方法中就可以编写希望线程完成的事务。实现Runnable接口优于通过继承Thread类方法。
此时我们只创建了一个线程,并不是多线程。
线程的基本操作
- 设置deamon/non-deamon属性
- 启动/停止线程
- 挂起/恢复线程 (通过调用stop方法,线程可能会停止;调用supend方法可能挂起线程;通过调用resume方法,另一个运行的程序可以恢复被挂起的线程。但这些方法在J2SE开始就被抛弃了,因为很容易就造成死锁。)
- 让步操作
- yield方法
- 等待其他对象(针对其他运行的线程调用obj.wait()方法,线程可能在等待某个对象的通知。obj只想当前线程想要的等待的对象。)
- 中断/打扰线程(三个方法:interrupt、isInterrupted和interrupted.)
线程同步
以上只是创建线程和线程的一些基础知识,并不涉及多线程。假如有两筐求,两个非常无聊人做着相反的事情,一个将球从左边一堆移到右面,而另一个相反。正常情况下,这两个人玩够之后两框球的总数应该是不变的。用一个线程模拟一个人,程序如下:
public class BucketBallGame{ private int bucket[] = {10000, 10000}; private static boolean RIGHT_TO_LEFT; public static void main(String[] args){ new BucketBallGame().doTransfers(); } private void doTransfers(){ for(int i=0; i < 10; i++){ new Thread(new TransferThread(!RIGHT_TO_LEFT)).start(); new Thread(new TransferThread(RIGHT_TO_LEFT)).start(); } } public void transfer(boolean direction, int numToTransfer){ if(direction == RIGHT_TO_LEFT){ bucket[0] += numToTransfer; bucket[1] -= numToTransfer; }else{ bucket[0] -= numToTransfer; bucket[1] += numToTransfer; } System.out.println("Bucket_Right: "+bucket[0]+" Bucket_Left: "+bucket[1]+" Total: "+(bucket[0]+bucket[1])); } private class TransferThread implements Runnable{ private boolean direction; public TransferThread(boolean direction){ this.direction = direction; } @Override public void run(){ for(int i=0; i < 10; i++){ transfer(direction, (int)(Math.random()*2000)); try{ Thread.sleep((int)(Math.random()*100)); }catch(InterruptedException ex){ } } } }}
但是运行的结果总和并不是常数20000,因为我梦并没有以原子单元来执行移出或者加入球的操作。解决的方法是在移动球是加一个同步关键字,如下:
public synchronized void transfer(boolean direction, int numToTransfer){
Java提供了特殊的等待/通知机制,以生产者/消费者问题为例:
public class ProducerConsumerGame{ public static void main(String args[]){ Bucket bucket = new Bucket(); new Thread(new Producer(bucket)).start(); new Thread(new Consumer(bucket)).start(); }}final class Consumer implements Runnable{ private Bucket bucket; public Consumer(Bucket bucket){ this.bucket = bucket; } @Override public void run(){ for(int i=0; i < 10; i++){ bucket.get(); } }}final class Producer implements Runnable{ private Bucket bucket; public Producer(Bucket bucket){ this.bucket = bucket; } @Override public void run(){ for(int i=0; i < 10; i++){ bucket.put((int)(Math.random()*100)); } }}class Bucket{ private int packOfBalls; private boolean available = false; public synchronized int get(){ if(available == false){ try{ wait(); }catch(InterruptedException e){ } } System.out.println("Consumer got: "+packOfBalls); available = false; notify(); return packOfBalls; } public synchronized void put(int packOfBalls){ if(available){ try{ wait(); }catch(InterruptedException e){ } } this.packOfBalls = packOfBalls; available = true; System.out.println("Producer put: "+packOfBalls); notify(); }}
生产者/消费者模型在现实生活场景中可以观察到,譬如聊天程序。
何时进行同步
在多线程中,通常多个线程要对共享的数据进行操作,所以同步是必不可少的。synchronized语义保证在任意给定的时间内,只有一个线程能访问受保护区段。但这往往会带来性能问题。所以但多线程程序需要足够的同步以保护共享的数据又要避免他们受到破坏,但又不要过分的同步,可以考虑使用volatile关键字实现同步。
Java线程初探