- 浏览: 680817 次
- 性别:
- 来自: 安徽
-
文章分类
- 全部博客 (252)
- Html/Div+CSS (12)
- Js/Jquery (34)
- Flex (2)
- Ajax (3)
- Java (35)
- C# (15)
- Spring (16)
- Hibernate (13)
- Struts2 (12)
- Struts1 (7)
- DWR (1)
- iBatis/myBatis (9)
- Tag(JSTL、EL) (1)
- Android (44)
- SQL (7)
- SEO (7)
- Exception (3)
- Tool (10)
- Other (3)
- WebService (9)
- Apache (7)
- Ext (0)
- Utils (12)
- thinking in programme (2)
- Hadoop (0)
- ActiveMQ (0)
- HTML5/CSS3 (0)
- WPF (1)
- NodeJs (1)
- 设计模式 (0)
- 程序人生 (1)
- 随笔 (1)
- Linux (1)
- Load Balance (0)
最新评论
-
drinkjava2:
太复杂了而且不通用,利用ThreadLocal可完美解决这一问 ...
JDBC的多条件动态查询 -
u013107014:
multipartRequest.getFiles(" ...
多文件上传 by MultipartFile and Multiple -
liyys:
可惜没讲你mysql数据库的表的设计
iBatis入门 -
Mapple_leave:
效果还是挺不错的,谢谢了。
中文简体与繁体的转换 -
arcpad:
JS禁用浏览器退格键
今天准备总结一下关于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关键字声明的方法没有方法体
说明:操作系统有很多种,Windows 、Linux 、UNIX ,既然多线程操作中要进行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 、生产者和消费者问题:
首先简单说明一下sleep 、wait 、notify 的区别:
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 接口的子类。
在实际的开发中比如说发多个邮件提醒等都会用到线程的,所以线程还是很重要的;
发表评论
-
log4j xml配置详解
2014-06-18 10:37 1166<?xml version="1.0&qu ... -
test
2013-07-29 09:16 0private static CacheImpl insta ... -
多文件上传 by MultipartFile and Multiple
2012-12-03 09:13 14364最近的一个项目中,需要用到 ... -
布局框架-SiteMesh
2012-11-30 08:57 2603最近在一个项目中使用 ... -
Servlet
2012-12-07 08:36 1565一、认识 Servlet : ... -
连接池
2012-12-10 08:42 1458... -
忆Java String
2012-11-15 08:38 1287平时 .NET 写多了, Java ... -
Eclipse中右键快速定位文件资源的工具
2012-11-09 08:43 2030当你开发.NET项目后,使用VS习惯了再来使用Java和Ecl ... -
Java中MessageFormat对象实现格式化字符串输出,类似C#中的string.format方法
2012-09-29 11:39 3684平时.NET做多了,再做Java时,总会进行一些比较,比如说J ... -
byte[]与InputStream互转
2012-09-29 11:39 1597InputStream转byte[] private ... -
统一中英文长度问题
2012-07-20 00:17 1950最近因为在做一个项目要求很多都是英文,所以这就涉及到在页 ... -
Java中Process的waitFor()阻塞问题
2012-07-21 01:00 8973在做视频转换时,调用外部的 exe 去进行一些视频 ... -
Tomcat注册成系统服务
2012-07-17 00:00 1574为了部署项目后不出现黑色的 doc 命令框,所以很 ... -
urlrewrite实现伪静态化
2012-07-25 00:41 3233产生背景 静态网页与动态网页比较: ... -
Java中java.util.Date时间和java.sql.Date时间的相互转化
2012-01-30 22:49 2977刚刚写用 JS 禁用退格键时( http ... -
使用BeanUtils类简化request和resultset转换
2012-01-21 20:23 2995当提交表单时,如果没有使用Struts等框架的话,你的代 ... -
JDBC的多条件动态查询
2012-01-19 11:05 7219前面我已经提到了现在的项目无非就是列表里的分页加多条件查 ... -
JDBC分页
2012-01-19 10:15 5549不知道大家做项目做到最后有什么感觉没有,其实大家做来做去 ... -
Java农历(阴历)工具类
2012-01-20 11:30 2380在真实的项目开发中会可能会遇到项目需要对阴历即我们所说的农历节 ... -
Eclipse中java项目引用dll库的路径设置(System.loadLibrary()调用Dll路径问题)
2012-01-16 14:13 4286右击项目名|选择属性properties|在左边列表内选择“J ...
相关推荐
程研究了线程的基础知识— 线程是什么、线程为什么有用以及怎么开始编写使用线程的简单 程序。 我们还将研究更复杂的、使用线程的应用程序的基本构件— 如何在线程之间交换数据、如何控制 线程以及线程如何互相通信...
Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...
NULL 博文链接:https://yangeoo.iteye.com/blog/2219492
Java线程堆栈详解 Java线程堆栈是一种强大的诊断工具,能够帮助开发者快速定位多线程应用程序中的问题。通过分析线程堆栈,可以找到系统中各种问题的根源,如系统无缘无故CPU过高、系统挂起、系统运行越来越慢、...
深入理解 Java 线程通信 Java 线程通信是 Java 编程中一个非常重要的概念,它可以使得多个线程之间能够相互通信和协作,从而实现更加复杂的任务。下面我们将深入理解 Java 线程通信的机制和实现方式。 一、等待...
Java线程状态流转图知识点总结 Java线程状态流转图是一种用于描述Java线程生命周期中不同的状态和状态转换的图形表示方式。该图形展示了Java线程从创建到终止的整个生命周期,并详细介绍了每种状态的特点和转换...
Java线程的优先级范围是从1(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY),默认情况下,每个新创建的线程都继承其父线程的优先级,通常是5(Thread.NORM_PRIORITY)。在Java中,可以使用`setPriority()`方法...
Java线程组操作实例分析 Java线程组操作实例分析主要介绍了Java线程组操作,结合实例形式分析了ThreadGroup类创建与使用线程组相关操作技巧。下面是对Java线程组操作的详细解释。 一、ThreadGroup类的作用 Thread...
Java线程同步操作实例详解 Java线程同步操作是指在多线程编程中,为了避免线程之间的数据不一致和混乱,采取的一种机制,以确保线程安全。Java提供了多种方式来实现线程同步,包括synchronized关键字、Lock接口、...
Java线程间同步互斥,在实际的编程中,经常要处理线程间的同步互斥问题。Java 语言内在强大的多线程支持使得处理这类问题变得相对来说比较简单。本例将模仿经典的线程同步互斥例子——生产者和消费者问题,来演示 ...
Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个...
"Java线程关闭的3种方法" Java线程关闭是Java编程中非常重要的一个话题,今天我们将介绍Java线程关闭的3种方法。 第一种方法:使用状态位 使用状态位是Java线程关闭的一种简单方法。我们可以使用一个volatile的...
首先,Java并发的问题要从JMM(JavaMemoryModel)讲起,先上一张JMM的结构图:在Java内存模型中,分为主内存和线程工作内存,线程使用共享数据时,都是先从主内存中拷贝到工作内存,使用完成之后再写入主内存,可以...
本文将详细探讨Java线程的相关知识点,特别是基于给定的标题“java 线程1”和描述。 首先,线程有多种状态,包括新建(New)、可运行(Runnable)、运行(Running)、阻塞(Blocked)、等待(Waiting)和终止(Terminated)。在...
java 线程状态、线程池 1. java 的线程状态 状态 发生条件 NEW 线程刚刚被创建,没有启动,没有调用start方法 RUNNABLE(可运行) 线程已经在JVM中运行,但是是否运行不确定,看当前线程是否由CPU执行权 ...
Java线程优先级是Java多线程编程中的一个重要概念,它涉及到如何更公平或高效地分配CPU资源。在Java中,线程的优先级被用来影响线程调度,但并不保证绝对的执行顺序,而是增加了高优先级线程被调度的概率。 Java...
Java线程监听、意外退出线程后自动重启的实现方法 Java线程监听是Java编程中的一种重要机制,用于监控和管理线程的生命周期。在实际开发中,我们经常会遇到线程意外退出的情况,例如网络异常、资源不足等原因导致...
《Java线程(第三版)》是一本深入探讨Java线程技术的专业书籍,旨在帮助开发者理解和掌握Java平台上的多线程编程。Java线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而充分利用系统资源,提高程序的...