`

Java线程

    博客分类:
  • 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

package com.iflytek.thread;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class MyThread extends Thread {

	private String name;

	public MyThread(String name) {
		this.name = name;
	}

	public void run() {// 覆写run()方法
		for (int i = 0; i < 10; i++) {
			System.out.println("Thread运行:" + name + ",i=" + i);
		}
	}

}

 

下面我们来实现上面的多线程操作类,MyThreadTest.java

package com.iflytek.thread;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class MyThreadTest {

	public static void main(String[] args) {
		MyThread thread1 = new MyThread("线程A");
		MyThread thread2 = new MyThread("线程B");

		thread1.run();// 调用线程
		thread2.run();
	}
	
}

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

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

package com.iflytek.thread;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class MyThreadTest {

	public static void main(String[] args) {
		MyThread thread1 = new MyThread("线程A");
		MyThread thread2 = new MyThread("线程B");

		thread1.start();// 调用线程
		thread2.start();
	}
	
}

 

这时再去执行发现结果有交互的现象,所以这也值得我们思考为什么非要使用start() 方法启动多线程呢?通过查看Java 源码:

public synchronized void start() {//定义start方法
	        /**
		 * This method is not invoked for the main method thread or "system"
		 * group threads created/set up by the VM. Any new functionality added 
		 * to this method in the future may have to also be added to the VM.
		 *
		 * A zero status value corresponds to state "NEW".
	         */
	        if (threadStatus != 0 || this != me)//判断线程是否已经启动
	            throw new IllegalThreadStateException();
	        group.add(this);
	        start0();//调用start0方法
	        if (stopBeforeStart) {
		    stop0(throwableFromStop);
		}
	    }

	    private native void start0();//使用native关键字声明的方法没有方法体

 

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

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

 

实现Runnable 接口:

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

 

查看源码发现Runnable 的定义:

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

 

所以一个类只要实现了此接口,并覆写run() 方法

 

package com.iflytek.thread;

/**
 * 
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class MyThreadByRunnable implements Runnable {

	private String name;

	public MyThreadByRunnable(String name) {
		this.name = name;
	}

	public void run() {// 覆写run()方法
		for (int i = 0; i < 10; i++) {
			System.out.println("Thread运行:" + name + ",i=" + i);
		}
	}
}

 

有了多线程操作类下面我们需要启动多线程,但是在现在使用Runnable 定义的子类中并没有start() 方法,而只有Thread 类中才有,在Thread 类中存在以下的一个构造方法:

 public Thread(Runnable target) {
	init(null, target, "Thread-" + nextThreadNum(), 0);
    }

 

此构造方法接受Runnable 的子类实例,也就是说现在我们可以通过Thread 类来启动Runnable 实现的多线程。

 

package com.iflytek.thread;

/**
 * 
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class MyThreadByRunnableTest {
	public static void main(String[] args) {

		MyThreadByRunnable thread1 = new MyThreadByRunnable("线程A");
		MyThreadByRunnable thread2 = new MyThreadByRunnable("线程B");
		new Thread(thread1).start();
		new Thread(thread2).start();

	}

}

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

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

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

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

2、 适合于资源的共享

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

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

package com.iflytek.thread;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class ThreadSyncDemo implements Runnable {
	Timer timer = new Timer();

	public static void main(String[] args) {
		ThreadSyncDemo threadSyncDemo = new ThreadSyncDemo();
		Thread thread1 = new Thread(threadSyncDemo);
		Thread thread2 = new Thread(threadSyncDemo);
		thread1.setName("t1");// 修改线程名称
		thread2.setName("t2");
		thread1.start();
		thread2.start();
	}

	@Override
	public void run() {
		timer.add(Thread.currentThread().getName());
	}
}

class Timer {
	private static int num = 0;

	public void add(String name) {
		num++;
		try {
			// 第一个线程执行到 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了
			Thread.sleep(1);
		} catch (InterruptedException e) {
		}
		System.out.println(name + ",你是第" + num + "个使用timer的线程");
	}
}

 

运行结果:

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

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

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

package com.iflytek.thread;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class ThreadSyncDemo02 implements Runnable {
	Timer02 timer = new Timer02();

	public static void main(String[] args) {
		ThreadSyncDemo02 threadSyncDemo = new ThreadSyncDemo02();
		Thread thread1 = new Thread(threadSyncDemo);
		Thread thread2 = new Thread(threadSyncDemo);
		thread1.setName("t1");// 修改线程名称
		thread2.setName("t2");
		thread1.start();
		thread2.start();
	}

	@Override
	public void run() {
		timer.add(Thread.currentThread().getName());
	}
}

class Timer02 {
	private static int num = 0;

	public synchronized void add(String name) {// 执行这个方法的过程之中,当前对象被锁定
		synchronized (this) {// 这样的话,在{}中的线程执行的过程中不会被另一个线程打断,也就是说{}只能有一个线程
			num++;
			try {
				Thread.sleep(1);// 第一个线程执行到
								// 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了
			} catch (InterruptedException e) {
			}
			System.out.println(name + ",你是第" + num + "个使用timer的线程");
		}
	}
}

运行结果:

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

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

2 、死锁,ThreadDieDemo.java

package com.iflytek.thread;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class ThreadDieDemo {

	public static void main(String[] args) {
		DeadLock lock1 = new DeadLock();
		DeadLock lock2 = new DeadLock();
		lock1.flag = 1;
		lock2.flag = 2;
		Thread thread1 = new Thread(lock1);
		Thread thread2 = new Thread(lock2);
		thread1.start();
		thread2.start();
	}
}

class DeadLock implements Runnable {
	public int flag = 1;
	static Object o1 = new Object();
	static Object o2 = new Object();

	@Override
	public void run() {
		System.out.println("flag = " + flag);
		if (flag == 1) {
			synchronized (o1) {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (o2) {
					System.out.println("o2");
				}
			}
		}
		if (flag == 2) {
			synchronized (o2) {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (o1) {
					System.out.println("o1");
				}
			}
		}
	}
}

 

 

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

首先简单说明一下sleepwaitnotify 的区别:

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

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

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

ProductCustomerDemo.java

package com.iflytek.thread;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class ProductCustomerDemo {
	public static void main(String[] args) {
		WoToStack woToStack = new WoToStack();
		Product product = new Product(woToStack);
		Customer customer = new Customer(woToStack);
		new Thread(product).start();
		new Thread(customer).start();
	}
}

/**
 * 消费和生产的对象
 * 
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
class WoTo {
	int id;

	public WoTo(int id) {
		this.id = id;
	}

	@Override
	public String toString() {
		return "WoTo [id=" + id + "]";
	}
}

class WoToStack {
	int index = 0;
	WoTo[] arrayWoTo = new WoTo[10];// 这里限制一下,框子最多装10个WoTo

	/**
	 * 向框子中放WoTo
	 * 
	 * @param wt
	 */
	public synchronized void push(WoTo wt) {
		// 这里用while是因为如果被打断还要执行判断,而如果是if则会直接进入下一个语句
		while (index == arrayWoTo.length) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notifyAll();
		arrayWoTo[index] = wt;
		index++;
	}

	/**
	 * 从框子中去WoTo
	 * 
	 * @return
	 */
	public synchronized WoTo pop() {
		while (index == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notifyAll();
		index--;
		return arrayWoTo[index];
	}
}

/**
 * 生产者
 * 
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
class Product implements Runnable {
	// 首先生产者需要知道生产WoTo放在哪里
	WoToStack stack = null;

	public Product(WoToStack stack) {
		this.stack = stack;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {// 这里我们限制一下每一个生产者可以生产20个WoTo
			WoTo woTo = new WoTo(i);
			stack.push(woTo);
			System.out.println("生产者生产了 :" + woTo);
			try {
				Thread.sleep((int) Math.random() * 200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class Customer implements Runnable {
	WoToStack stack = null;

	public Customer(WoToStack stack) {
		this.stack = stack;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {// 这里我们也限制一下每一个小费者可以消费20个WoTo
			WoTo woTo = stack.pop();
			System.out.println("消费者消费了 :" + woTo);
			try {
				Thread.sleep((int) Math.random() * 1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

 

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

MyThread.java:

package com.iflytek.maipiao;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class MyThread extends Thread {

	private int ticket = 5;// 一共5张票

	public void run() {
		for (int i = 0; i < 50; i++) {
			if (this.ticket > 0) {
				System.out.println("卖票:ticket = " + this.ticket--);
			}
		}
	}

}

 

下面建三个线程对象,同时卖票,ThreadTicket.java

package com.iflytek.maipiao;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class ThreadTicket {

	public static void main(String[] args) {
		MyThread thread1 = new MyThread();
		MyThread thread2 = new MyThread();
		MyThread thread3 = new MyThread();

		// 开始卖票
		thread1.start();
		thread2.start();
		thread3.start();
	}

}

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

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

MyThreadByRunnable.java

package com.iflytek.maipiao;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class MyThreadByRunnable implements Runnable {

	private int ticket = 5;// 一共5张票

	public void run() {
		for (int i = 0; i < 50; i++) {
			if (this.ticket > 0) {
				System.out.println("卖票:ticket = " + this.ticket--);
			}
		}
	}

}

同样,我们再弄一个多线程进行卖票的操作,RunnableTicket.Java

package com.iflytek.maipiao;

/**
 * @author xudongwang 2012-1-1
 * 
 *         Email:xdwangiflytek@gmail.com
 */
public class RunnableTicket {

	public static void main(String[] args) {
		MyThreadByRunnable threadByRunnable = new MyThreadByRunnable();
		new Thread(threadByRunnable).start();
		new Thread(threadByRunnable).start();
		new Thread(threadByRunnable).start();
	}
}

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

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

Public class Thread implements Runnable {

 

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

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

 

 

  • 大小: 6.9 KB
7
2
分享到:
评论
1 楼 小林信仁 2014-05-11  
总结的不错!

相关推荐

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition.Sep.2004.eBook-DDU Java Thread Programming (Sams) java线程第二版中英文 java线程第二版中英文 ...

    Java线程状态流转图

    Java线程状态流转图知识点总结 Java线程状态流转图是一种用于描述Java线程生命周期中不同的状态和状态转换的图形表示方式。该图形展示了Java线程从创建到终止的整个生命周期,并详细介绍了每种状态的特点和转换...

    java 线程 dump 分析工具 2.3.3

    java 线程Dump 分析工具: Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的...

    java线程.pdf

    根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...

    java线程分析工具TDA

    Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...

    Java线程详解大全

    Java线程是并发编程的核心部分,它允许程序在同一时间执行多个独立的任务,从而提高系统效率和响应速度。本文将深入探讨Java线程的概念、生命周期、实现方式以及相关的同步机制。 首先,理解线程的基本概念至关重要...

    java线程实例 各种小Demo

    Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而提高系统效率和响应速度。在Java中,线程可以分为用户线程和守护线程,前者是程序运行的基础,而后者是在所有用户线程结束时才终止的后台...

    Java线程使用教程

    Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的并发性和效率。本教程将深入探讨Java线程的使用,帮助开发者掌握这一关键技术。 一、线程基础 1. **线程的概念**:线程...

    Java线程.ppt

    Java线程是Java编程中的重要概念,特别是在多核处理器和并发处理中不可或缺。Java线程允许程序在同一时间执行多个不同的任务,从而提高了程序的效率和响应性。在燕山大学信息学院计算机系的课程中,李峰教授讲解了...

    java线程深入解析

    Java线程是Java编程语言中的核心概念,尤其在多任务处理和并发编程中扮演着重要角色。线程允许一个程序内部同时执行多个独立的控制流,使得程序能够更高效地利用处理器资源。本文将深入解析Java线程的相关知识点,...

    Java线程(第三版)

    《Java线程(第三版)》是一本深入探讨Java线程技术的专业书籍,旨在帮助开发者理解和掌握Java平台上的多线程编程。Java线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而充分利用系统资源,提高程序的...

    java线程入门级书籍

    ### Java线程入门知识点详解 #### 一、Java线程基础知识概述 **1.1 什么是线程?** 线程是程序执行流的最小单元,是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。在Java中...

    java线程文档大全

    Java线程是多任务编程中的核心概念,它允许程序同时执行多个不同的任务,极大地提高了程序的效率和响应性。在Java中,线程是通过Java.lang.Thread类或实现Runnable接口来创建和管理的。这份“java线程文档大全”包含...

    JAVA线程dump的分析

    JAVA线程dump的分析 JAVA线程dump是指在JAVA程序中,当前线程的状态和调用堆栈的快照,能够帮助开发者了解当前程序的执行情况,诊断问题和性能瓶颈。生成JAVA线程dump的方法在不同的操作系统下是不同的,在Windows...

    Java线程培训资料

    ### Java线程培训资料知识点详解 #### 一、Java线程基本概念 1. **如何编写与启动线程** - **方式一:继承Thread类** ```java class MyThread extends Thread { @Override public void run() { // 业务逻辑 ...

    Java-Thread-Affinity,将Java线程绑定到给定的内核.zip

    Java线程亲和性(Thread Affinity)是一个高级并发编程概念,主要涉及到操作系统调度和硬件资源的优化。在多核处理器系统中,线程亲和性允许开发者指定某个线程应该运行在哪个特定的处理器核心上,从而提高性能、...

    java 线程工具类 java 线程工具类

    java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具...

    4种常用Java线程锁的特点,性能比较、使用场景.pdf

    4种常用Java线程锁的特点,性能比较、使用场景 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发...

    JAVA线程学习(源代码)

    本资源"JAVA线程学习(源代码)"提供了关于Java线程的源代码示例,帮助我们深入理解和实践线程的使用。 首先,我们要理解Java中的线程模型。Java线程由`java.lang.Thread`类或`java.util.concurrent.Executor`框架来...

    java线程状态转换图

    Java 线程状态转换图 Java 线程状态转换图是 Java 编程中非常重要的一个概念,它描述了线程在不同的状态之间的转换关系。了解线程状态转换图对 Java 编程的理解和应用非常重要。本文将详细介绍 Java 线程状态转换图...

Global site tag (gtag.js) - Google Analytics