Java线程总结

今天准备总结一下关于Java 线程的问题,提到线程很容易与进程混淆,从计算机操作系统的发展来看,经历了这样的两个阶段:

       单进程处理:最早以前的DOS 系统就属于单进程处理,即:在同一个时间段上只能有一个程序在执行,所以在DOS 系统中只要有病毒的出现,则立刻会有反映;

       多进程处理:我们现在使用的Windows 操作系统就是典型的一个多线程,所以,如果在windows 中出现病毒了,则系统照样可以使用,通过Ctrl+Shift+delete 可以查看windows 系统的具体进程情况;

       那么对于资源来讲,所有的IO 设备、CPU 等等只有一个,那么对于多线程的处理来讲,在同一个时间段上会有多个程序运行,但是在同一个时间点 上只能有一个程序运行。所以我们可以发现线程是在进程的基础上进一步的划分,我们可以举个这样的例子,Eclipse 中对Java 的关键字的检查,是在Eclipse 整个程序运行中检测运行的。因此进程中止了,线程也随之中止。但是线程中止了,进程可能依然会执行。我们可以这样理解,进程是一个静态的概念,一个任务或者说一个程序,一个进程里有一个主线程。

       下面我们来看看Java 中对线程处理机制的支持,在Java 语言中对线程的实现有两种方恨死:一个是继承Thread 类,另一个是实现Runnable 接口。下面我们来分别来看看这两种实现方式:      

继承Thread 类:

       一个java 类只要继承了Thread 类   ,同时覆写了本类中的run() 方法,则就可以实现Java 中的多线程操作了。

MyThread.java :

01package com.iflytek.thread;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public classMyThread extends Thread {
09 
10    privateString name;
11 
12    publicMyThread(String name) {
13        this.name = name;
14    }
15 
16    publicvoid run() {// 覆写run()方法
17        for(int i = 0; i < 10; i++) {
18            System.out.println("Thread运行:"+ name + ",i=" + i);
19        }
20    }
21 
22}
下面我们来实现上面的多线程操作类,MyThreadTest.java :
01package com.iflytek.thread;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class MyThreadTest {
09 
10    public static void main(String[] args) {
11        MyThread thread1 = new MyThread("线程A");
12        MyThread thread2 = new MyThread("线程B");
13 
14        thread1.run();// 调用线程
15        thread2.run();
16    }
17     
18}

通过运行结果,我们可以发现其执行的结果非常有规律,先执行完第一个对象,再执行完第二个对象的,即没有实现交互的现象;

通过JDK 文档可以发现,一旦我们调用Start() 方法,则会通过JVM 找到run() 方法。所以当我们将上面调用的run() 方法改为start() 方法:

01package com.iflytek.thread;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class MyThreadTest {
09 
10    public static void main(String[] args) {
11        MyThread thread1 = new MyThread("线程A");
12        MyThread thread2 = new MyThread("线程B");
13 
14        thread1.start();// 调用线程
15        thread2.start();
16    }
17     
18}
这时再去执行发现结果有交互的现象,所以这也值得我们思考为什么非要使用start() 方法启动多线程呢?通过查看Java 源码:
01public synchronized void start() {//定义start方法
02            /**
03         * This method is not invoked for the main method thread or "system"
04         * group threads created/set up by the VM. Any new functionality added
05         * to this method in the future may have to also be added to the VM.
06         *
07         * A zero status value corresponds to state "NEW".
08             */
09            if (threadStatus != 0|| this != me)//判断线程是否已经启动
10                throw new IllegalThreadStateException();
11            group.add(this);
12            start0();//调用start0方法
13            if (stopBeforeStart) {
14            stop0(throwableFromStop);
15        }
16        }
17 
18        private native voidstart0();//使用native关键字声明的方法没有方法体

说明:操作系统有很多种,Windows 、Linux 、UNIX ,既然多线程操作中要进行CPU 资源的强占,也就是说要等待CPU 调度,那么这些调度的操作是由各个操作系统的底层实现的,所以在Java 程序中根本就没法实现,那么此时Java 的设计者定义了native 关键字,使用此关键字表示可以调用操作系统的底层函数,那么这样的技术又称为JNI 技术(Java Native Interface ),而且,此方法在执行的时候将调用run 方法完成,由系统默认调用的。

下面我们看看线程的状态:

7751f5b5-0065-3e0f-aa81-c8f5db51e8f3.png

 

实现Runnable 接口:

       因为我们知道继承的单一继承的局限性,所以我们在开发中一个多线程的操作类很少去使用Thread 类完成,而是通过Runnable 接口完成。

 

       查看源码发现Runnable 的定义:

1public interface Runnable {
2    public abstract void run();
3}
所以一个类只要实现了此接口,并覆写run() 方法
01package com.iflytek.thread;
02 
03/**
04 *
05 * @author xudongwang 2012-1-1
06 *
07 *         Email:xdwangiflytek@gmail.com
08 */
09public class MyThreadByRunnable implementsRunnable {
10 
11    private String name;
12 
13    public MyThreadByRunnable(String name) {
14        this.name = name;
15    }
16 
17    public void run() {// 覆写run()方法
18        for (int i =0; i < 10; i++) {
19            System.out.println("Thread运行:"+ name + ",i=" + i);
20        }
21    }
22}
有了多线程操作类下面我们需要启动多线程,但是在现在使用Runnable 定义的子类中并没有start() 方法,而只有Thread 类中才有,在Thread 类中存在以下的一个构造方法:
1public Thread(Runnable target) {
2   init(null, target, "Thread-"+ nextThreadNum(), 0);
3   }
此构造方法接受Runnable 的子类实例,也就是说现在我们可以通过Thread 类来启动Runnable 实现的多线程。
01package com.iflytek.thread;
02 
03/**
04 *
05 * @author xudongwang 2012-1-1
06 *
07 *         Email:xdwangiflytek@gmail.com
08 */
09public class MyThreadByRunnableTest {
10    public static void main(String[] args) {
11 
12        MyThreadByRunnable thread1 = newMyThreadByRunnable("线程A");
13        MyThreadByRunnable thread2 = newMyThreadByRunnable("线程B");
14        new Thread(thread1).start();
15        new Thread(thread2).start();
16 
17    }
18 
19}

当然上面的操作代码也属于交替的运行,所以此时程序也同样实现了多线的操作;

 

下面我们来总结一下两种实现方式的区别及联系:

       在程序的开发中只要是多线程则肯定永远以实现Runnable 接口为正统操作,因为实现Runnable 接口相比继承Thread 类有如下的好处:

1、 避免单继承的局限性,一个类可以同时实现多个接口

2、 适合于资源的共享

 

下面来说说关于线程的几个Demo ;

1 、两个线程访问同一个对象,ThreadSyncDemo.java :

01package com.iflytek.thread;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class ThreadSyncDemo implementsRunnable {
09    Timer timer = new Timer();
10 
11    public static void main(String[] args) {
12        ThreadSyncDemo threadSyncDemo = newThreadSyncDemo();
13        Thread thread1 = new Thread(threadSyncDemo);
14        Thread thread2 = new Thread(threadSyncDemo);
15        thread1.setName("t1");// 修改线程名称
16        thread2.setName("t2");
17        thread1.start();
18        thread2.start();
19    }
20 
21    @Override
22    public void run() {
23        timer.add(Thread.currentThread().getName());
24    }
25}
26 
27class Timer {
28    private static int num = 0;
29 
30    public void add(String name) {
31        num++;
32        try {
33            // 第一个线程执行到 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了
34            Thread.sleep(1);
35        } catch (InterruptedException e) {
36        }
37        System.out.println(name + ",你是第"+ num + "个使用timer的线程");
38    }
39}

运行结果:

t1, 你是第 2 个使用 timer 的线程

t2, 你是第 2 个使用timer 的线程

而如果程序这样改动一下,ThreadSyncDemo02.java :

01package com.iflytek.thread;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class ThreadSyncDemo02 implementsRunnable {
09    Timer02 timer = new Timer02();
10 
11    public static void main(String[] args) {
12        ThreadSyncDemo02 threadSyncDemo = newThreadSyncDemo02();
13        Thread thread1 = new Thread(threadSyncDemo);
14        Thread thread2 = new Thread(threadSyncDemo);
15        thread1.setName("t1");// 修改线程名称
16        thread2.setName("t2");
17        thread1.start();
18        thread2.start();
19    }
20 
21    @Override
22    public void run() {
23        timer.add(Thread.currentThread().getName());
24    }
25}
26 
27class Timer02 {
28    private static int num = 0;
29 
30    public synchronized voidadd(String name) {// 执行这个方法的过程之中,当前对象被锁定
31        synchronized (this) {// 这样的话,在{}中的线程执行的过程中不会被另一个线程打断,也就是说{}只能有一个线程
32            num++;
33            try {
34                Thread.sleep(1);// 第一个线程执行到
35                                // 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了
36            } catch (InterruptedException e) {
37            }
38            System.out.println(name + ",你是第"+ num + "个使用timer的线程");
39        }
40    }
41}
运行结果:

t1, 你是第 1 个使用 timer 的线程

t2, 你是第 2 个使用timer 的线程

 

2 、死锁,ThreadDieDemo.java :

01package com.iflytek.thread;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class ThreadDieDemo {
09 
10    public static void main(String[] args) {
11        DeadLock lock1 = new DeadLock();
12        DeadLock lock2 = new DeadLock();
13        lock1.flag = 1;
14        lock2.flag = 2;
15        Thread thread1 = new Thread(lock1);
16        Thread thread2 = new Thread(lock2);
17        thread1.start();
18        thread2.start();
19    }
20}
21 
22class DeadLock implements Runnable {
23    public int flag = 1;
24    static Object o1 = newObject();
25    static Object o2 = newObject();
26 
27    @Override
28    public void run() {
29        System.out.println("flag = " + flag);
30        if (flag == 1) {
31            synchronized (o1) {
32                try {
33                    Thread.sleep(500);
34                } catch (InterruptedException e) {
35                    e.printStackTrace();
36                }
37                synchronized (o2) {
38                    System.out.println("o2");
39                }
40            }
41        }
42        if (flag == 2) {
43            synchronized (o2) {
44                try {
45                    Thread.sleep(500);
46                } catch (InterruptedException e) {
47                    e.printStackTrace();
48                }
49                synchronized (o1) {
50                    System.out.println("o1");
51                }
52            }
53        }
54    }
55}

3 、生产者和消费者问题:

首先简单说明一下sleep 、wait 、notify 的区别:

       sleep :sleep 是在Thread 中的,同时在sleep 的时候锁还在;

        wait :wait 必须是在锁住对象时才能wait ,同时在wait 的时候,锁就不在归那个对象所有了,而在其方法定义在Object 中,它是让进入到此锁住对象的线程wait ;

notify :与wait 相对应,叫醒一个现在正在wait 在我这个对象上的线程,谁现在正在我这个对象上等待,我就叫醒这个线程让他继续执行,他也是Object 类中的方法;

ProductCustomerDemo.java :

001package com.iflytek.thread;
002 
003/**
004 * @author xudongwang 2012-1-1
005 *
006 *         Email:xdwangiflytek@gmail.com
007 */
008public class ProductCustomerDemo {
009    public static void main(String[] args) {
010        WoToStack woToStack = new WoToStack();
011        Product product = new Product(woToStack);
012        Customer customer = new Customer(woToStack);
013        new Thread(product).start();
014        new Thread(customer).start();
015    }
016}
017 
018/**
019 * 消费和生产的对象
020 *
021 * @author xudongwang 2012-1-1
022 *
023 *         Email:xdwangiflytek@gmail.com
024 */
025class WoTo {
026    int id;
027 
028    public WoTo(int id) {
029        this.id = id;
030    }
031 
032    @Override
033    public String toString() {
034        return "WoTo [id=" + id +"]";
035    }
036}
037 
038class WoToStack {
039    int index = 0;
040    WoTo[] arrayWoTo = new WoTo[10];// 这里限制一下,框子最多装10个WoTo
041 
042    /**
043     * 向框子中放WoTo
044     *
045     * @param wt
046     */
047    public synchronized voidpush(WoTo wt) {
048        // 这里用while是因为如果被打断还要执行判断,而如果是if则会直接进入下一个语句
049        while (index == arrayWoTo.length) {
050            try {
051                this.wait();
052            } catch (InterruptedException e) {
053                e.printStackTrace();
054            }
055        }
056        this.notifyAll();
057        arrayWoTo[index] = wt;
058        index++;
059    }
060 
061    /**
062     * 从框子中去WoTo
063     *
064     * @return
065     */
066    public synchronized WoTo pop() {
067        while (index == 0) {
068            try {
069                this.wait();
070            } catch (InterruptedException e) {
071                e.printStackTrace();
072            }
073        }
074        this.notifyAll();
075        index--;
076        return arrayWoTo[index];
077    }
078}
079 
080/**
081 * 生产者
082 *
083 * @author xudongwang 2012-1-1
084 *
085 *         Email:xdwangiflytek@gmail.com
086 */
087class Product implements Runnable {
088    // 首先生产者需要知道生产WoTo放在哪里
089    WoToStack stack = null;
090 
091    public Product(WoToStack stack) {
092        this.stack = stack;
093    }
094 
095    @Override
096    public void run() {
097        for (int i =0; i < 20; i++) {// 这里我们限制一下每一个生产者可以生产20个WoTo
098            WoTo woTo = new WoTo(i);
099            stack.push(woTo);
100            System.out.println("生产者生产了 :"+ woTo);
101            try {
102                Thread.sleep((int) Math.random() *200);
103            } catch (InterruptedException e) {
104                e.printStackTrace();
105            }
106        }
107    }
108}
109 
110class Customer implements Runnable {
111    WoToStack stack = null;
112 
113    public Customer(WoToStack stack) {
114        this.stack = stack;
115    }
116 
117    @Override
118    public void run() {
119        for (int i =0; i < 20; i++) {// 这里我们也限制一下每一个小费者可以消费20个WoTo
120            WoTo woTo = stack.pop();
121            System.out.println("消费者消费了 :"+ woTo);
122            try {
123                Thread.sleep((int) Math.random() *1000);
124            } catch (InterruptedException e) {
125                e.printStackTrace();
126            }
127        }
128    }
129}

4 、卖票问题(Runnable 资源共享):

MyThread.java:

01package com.iflytek.maipiao;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class MyThread extendsThread {
09 
10    private int ticket = 5;// 一共5张票
11 
12    public void run() {
13        for (int i =0; i < 50; i++) {
14            if (this.ticket >0) {
15                System.out.println("卖票:ticket = "+ this.ticket--);
16            }
17        }
18    }
19 
20}
下面建三个线程对象,同时卖票,ThreadTicket.java :
01package com.iflytek.maipiao;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class ThreadTicket {
09 
10    public static void main(String[] args) {
11        MyThread thread1 = new MyThread();
12        MyThread thread2 = new MyThread();
13        MyThread thread3 = new MyThread();
14 
15        // 开始卖票
16        thread1.start();
17        thread2.start();
18        thread3.start();
19    }
20 
21}

运行发现一共卖了15 张票,但是实际上只有5 张票,所以证明每一个线程都卖自己的票,这样就没有达到资源共享的目的。

其实我们使用Runnable 接口的话,则就可以实现资源的共享:

MyThreadByRunnable.java :

01package com.iflytek.maipiao;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class MyThreadByRunnable implementsRunnable {
09 
10    private int ticket = 5;// 一共5张票
11 
12    public void run() {
13        for (int i =0; i < 50; i++) {
14            if (this.ticket >0) {
15                System.out.println("卖票:ticket = "+ this.ticket--);
16            }
17        }
18    }
19 
20}
同样,我们再弄一个多线程进行卖票的操作,RunnableTicket.Java :
01package com.iflytek.maipiao;
02 
03/**
04 * @author xudongwang 2012-1-1
05 *
06 *         Email:xdwangiflytek@gmail.com
07 */
08public class RunnableTicket {
09 
10    public static void main(String[] args) {
11        MyThreadByRunnable threadByRunnable = newMyThreadByRunnable();
12        new Thread(threadByRunnable).start();
13        new Thread(threadByRunnable).start();
14        new Thread(threadByRunnable).start();
15    }
16}

虽然现在程序中有三个线程,但是从运行结果上看,三个线程一共卖出了5 张票,也就是说使用Runnable 实现的多线程可以达到资源共享的目的。

实际上,Runnable 接口和Thread 类之间还是存在联系的

1Public class Thread implementsRunnable {

发现Thread 类也是Runnable 接口的子类。

在实际的开发中比如说发多个邮件提醒等都会用到线程的,所以线程还是很重要的;


转自:http://xdwangiflytek.iteye.com/blog/1333128

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值