java多线程总结

2024-10-03

java多线程总结(通用10篇)

1.java多线程总结 篇一

1、进程和线程的基础知识

l 进程:运行中的应用程序称为进程,拥有系统资源(cpu、内存)

l 线程:进程中的一段代码,一个进程中可以哦有多段代码,本身不拥有资源(共享所在进程的资源)

在java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程。

区别: 1、是否占有资源问题

2、创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大。

3、进程为重量级组件,线程为轻量级组件

l 多进程: 在操作系统中能同时运行多个任务(程序)

l 多线程: 在同一应用程序中有多个功能流同时执行

2、线程的主要特点

l 不能以一个文件名的方式独立存在在磁盘中;

l 不能单独执行,只有在进程启动后才可启动;

l 线程可以共享进程相同的内存(代码与数据)。

3、线程的主要用途

l 利用它可以完成重复性的工作(如实现动画、声音等的播放)。

l 从事一次性较费时的初始化工作(如网络连接、声音数据文件的加载)。

l 并发执行的运行效果(一个进程多个线程)以实现更复杂的功能

4、多线程(多个线程同时运行)程序的主要优点

l 可以减轻系统性能方面的瓶颈,因为可以并行操作;

l 提高CPU的处理器的效率,在多线程中,通过优先级管理,可以使重要的程序优先操作,提高了任务管理的灵活性;另一方面,在多CPU系统中,可以把不同的线程在不同的CPU中执行,真正做到同时处理多任务。

2.java多线程总结 篇二

1、线程的生命周期

一个线程创建之后, 总处于某种状态之中, 线程的状态表示了线程正在进行的活动以及在这段时间内线程能完成的任务, 在线程的生命周期中有四种状态:创建新线程态、可运行态、不可运行态、死亡态, 通过对线程进行操作来改变其状态, 如下图所示。

1.1 创建状态

创建了一个线程而还没有启动它, 则处于创建状态, 此时仅是一个空的线程对象, 并不获得应有资源, 只有启动后, 系统才为它分配资源。处于创建状态的线程可以调用start () 方法启动, 使其进入可运行状态;也可调用stop () 方法, 使其进入死亡状态。

1.2 可运行状态

可运行状态只说明该线程具备了运行的条件, 但并不一定是运行状态, 因为在单处理器系统中运行多线程程序, 实际上在每个"时刻"至多有一个线程在运行, 而系统中可能有多个线程都处于可运行状态, 系统通过快速切换和调度使所有可运行的线程共享处理器, 造成宏观上的多线程并发运行。在可运行状态, 线程运行的是线程体, 线程体由run () 方法规定, 在自己定义的线程类中重写。

在可运行状态下可进行多种操作:调用suspend () 方法, 使线程挂起, 从而进入不可运行状态;调用sleep () 方法, 使线程睡眠, 从而进入不可运行状态;调用wait () 方法, 使线程等待, 从而进入不可运行状态;调用yield () 方法, 使线程退让, 把CPU控制权提前交给同级优先权的其他线程;调用stop () 方法, 使线程终止, 从而进入消亡状态。正常的情况下是执行完run () 方法, 使线程结束, 进入死亡状态。

1.3 不可运行状态

处于可运行状态的线程遇到下列情况进入不可运行状态:调用了suspend () 方法;调用了sleep () 方法;调用了wait () 方法;如果一个线程是和I/O操作有关的, 则在执行I/O指令时, 由于外设速度远远低于CPU速度而使线程受到阻塞, 此时也进入不可运行状态。

当线程处于不可运行状态时, 可通过下面途径恢复到可运行状态:通过sleep () 方法进入不可运行状态的线程, 在过了指定的睡眠时间后自动恢复;由于I/O阻塞而进入不可运行状态的线程在外设完成I/O操作后自动恢复;通过suspend () 方法进入不可运行状态的线程, 通过调用resume () 方法恢复;通过wai () 方法进入不可运行状态的线程, 通过调用notify () 或notifyAl () 方法恢复。在不可运行状态, 也可调用stop () 方法进入死亡状态。

1.4 死亡状态

有两种情况使线程进入死亡状态:从可运行状态执行完run () 方法, 自然撤消;从任何一种其它状态调用stop () 方法。从死亡状态不能转换到别的状态, 因为到此线程的生命周期就结束了。

2、线程的建立和使用

在Java程序中可通过对Thread类派生一个子类, 再由这个子类生成对象来实现线程的创建;也可直接定义一个类实现接口Runnable, 然后再由这个类生成对象从而创建线程。

2.1 继承类方式

用这种方式创建线程并使之进入可运行状态, 可参考下面步骤:

(1) 从Thread类派生一个子类。例如:

这就有了一名叫MyThread的线程的类, 用这个类每生成一个对象, 就创建一个线程。

(2) 在run方法中实现线程的功能。一个Thread类的内部结构使得总是执行线程本身的run方法。

(3) 生成类对象, 创建线程。例如:

My Thread myFristThread=new MyThread () ;

(4) 调用start方法, 使之运行。例如:

myFristThread.start () ;

另外在可运行状态中, 可以调用其它方法进入不可运行状态, 或再返回可运行状态, 也可调用stop方法使线程退出。

2.2 实现接口的方式

用继承类的方式创建线程比较方便, 但由于Java是单继承的, 如果定义的线程类已经继承了其它的类, 就不能再继承Thread了, 此时可用实现接口的方式。用这种方式创建线程与继承类的方式很相似, 只是创建新线程时增加中间过渡语句。

例:数字时钟, 显示当前系统时间, 每隔一秒钟刷新一次。

3、结束语

Java具有平台独立、面向对象、多线程、安全性等许多优点, 是目前最为优秀的编程语言之一。用其它一些语言也能实现多线程程序, 但必须靠外部的线程软件包来完成, 而Java本身是使用线程的思想开发的, 他的所有类都是在多线程的思想下定义的, 因而Java从语言级提供对多线程的支持。

摘要:多线程是Java的一个重要特点, 这使得在一个Java程序内部可同时进行多种运算, 从而充分利用系统资源, 提高程序运行效率。本文论述在Java程序中创建线程和实现线程体的机制。

关键词:Java,程序设计,多线程

参考文献

[1].Y.Daniel Liang, Introduction to Java Programming, 机械工业出版社, 2008年6月

[2].Bruce Eckel, Thinking in Java, 机械工业出版社, 2002年9月

[3].H.M.Deitel, P.J.Deitel, How to Java Program, 清华大学出版社, 2004年3月

[4].Horstmann Gay S, JAVA核心技术卷1, 机械工业出版社, 2008年

3.java多线程总结 篇三

【关键词】类;对象;继承;接口;多线程机制

1.问题描述

Java多线程机制实现了异步执行环境,线程间的通信简单有效,每个线程彼此独立执行,一个程序可以同时使用多个线程来完成不同任务。依此可实现在窗口中显示北京时间、格林尼治时间、夏威夷时间和东京时区四个时区的时钟。

2.问题实现

定义从Jpanel控件继承生成clock类并实现Runnable接口。在其构造方法中创建线程,通过其中的pain方法画出当前时区的时钟信息。在线程的run方法中检测当前CPU上线程是否为自身并让自身线程睡眠1000个时间单位,在主类DrawingCLock的方法中创建四个clock对象并加入到主类的frame控件中,从而实现四个时区的时钟同时显示。

3.实现程序代码

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import java.util.*;

public class DrawingClock {

JFrame frame;

clock panell,panel2,panel3,panel4;

public static void main(String args[]) {

DrawingClock dc=new DrawingClock();

dc.go();

}

public void go() {

frame=new JFrame("时钟");

frame.getContentPane().setLayout(null);

panell=new clock(160,150,"北京时间",0);//线程1画北京时间时钟

frame.getContentPane().add(panell);

panel2=new clock(400,150,"格林威治时间",-7);////线程2画北京时间时钟

frame.getContentPane().add(panel2);

panel3=new clock(160,450,"东京时间",1);//线程3画北京时间时钟

frame.getContentPane().add(panel3);

panel4 = new clock(400,450,"夏威夷时间",-10);//线程4画北京时间时钟

frame.getContentPane().add(panel4);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setSize(600, 650);

frame.setVisible(true);

}

}

class clock extends JPanel implements Runnable {

Thread thr1=new Thread(this);

String text;

int z;

Graphics graphics;

Date currentDate=new Date(); //获取当前日期信息

String lastdate=currentDate.toString(); //获取当前日期信息的字符串形式

int xcenter , ycenter; // 时钟的中心坐标值

int radius=100;// 时钟的半径

void drawCircle()

{graphics.drawArc(xcenter-radius, ycenter-radius, radius*2, radius*2,0,360);

for(int i=1;i<=12;i++)//画表盘数字

{int x=xcenter+(int)((radius-15)*Math.sin(i*Math.PI/6));

int y=ycenter-(int)((radius-15)*Math.cos(i*Math.PI/6));

String str=""+i;

graphics.drawString(str,x,y);

}

}

public void paint(Graphics g) {

graphics=g;

g.clearRect(xcenter-radius,ycenter-radius,radius*2,radius*2);

drawCircle();//画表盘

int xh, yh, xm, ym, xs, ys;

//定义变量用于存放当前的秒针、分针和时针的末端坐标值

int s,m,h;

currentDate= new Date(); //获取当前日期信息

String today=text+":"; //获取当前日期信息的字符串形式

Calendar nowcal= Calendar.getInstance();

s=nowcal.get(Calendar.SECOND);//获取当前的秒值

m=nowcal.get(Calendar.MINUTE);//获取当前的分钟值

h=nowcal.get(Calendar.HOUR);//获取当前的小时值

h+=z;

nowcal.set(Calendar.HOUR,h);

today+=nowcal.getTime().toString();

//获取当前秒针、分针和时针的末端坐标值

xs=(int) (Math.cos(s * Math.PI / 30 - Math.PI / 2) *(radius-10) + xcenter);

ys=(int) (Math.sin(s * Math.PI / 30 - Math.PI / 2) *(radius-10) + ycenter);

xm=(int) (Math.cos(m * Math.PI / 30 - Math.PI / 2) * (radius-40) + xcenter);

ym=(int) (Math.sin(m * Math.PI / 30 - Math.PI / 2) * (radius-40) + ycenter);

xh=(int) (Math.cos((h*30+m/2) * Math.PI / 180 - Math.PI / 2) * (radius-60)+ xcenter);

yh=(int) (Math.sin((h*30+m/2) * Math.PI / 180 - Math.PI / 2) * (radius-60)+ ycenter);

//清除界面,显示日期信息,并画秒针、分针和时针

g.clearRect(xcenter-100, ycenter+radius+2,200,30);

g.drawString(today, xcenter-100, ycenter+radius+20);

g.drawLine(xcenter, ycenter, xs, ys);

g.drawLine(xcenter, ycenter, xm, ym);

g.drawLine(xcenter, ycenter, xh, yh);

}

public void run() {

Thread me = Thread.currentThread();

while ((thr1== me)) {//若是本线程则休眠1000个单位后刷新

try {

Thread.currentThread().sleep(1000);//当前线程休眠1秒

} catch (InterruptedException e) {

}

repaint();//重画时钟

}

}

public clock(int x,int y,String s,int zone)

{

z=zone;

text=s;

xcenter=x;

ycenter=y;

setSize(600,800);

thr1.start();//启动线程

}

}

4.结语

本程序能准确显示四个时区的时间信息,但界面美观程度不够。可以利用Java的相关技术对时钟界面进行修改,使显示效果更好。

【参考文献】

[1]彭正文主编.Java程序设计.中国铁道出版社.

[2]谭浩强主编.Java程序设计.清华大学出版社.

4.Java线程及多线程技术及应用 篇四

方 法

功 能

isAlive()

判断线程是否还“活”着,即线程是否还未终止。

getPriority()

获得线程的优先级数值

setPriority()

设置线程的优先级数值

setName()

给线程一个名字

getName()

取得线程的名字

currentThread()

取得当前正在运行的线程对象,也就是取得自己本身

2、操作线程的基本信息代码示例

package com.px1987.j2se.thread.priority;

public class ThreadInfoTest {

public static void main(String[] argc) throws Exception {

Runnable r = new MyThread();

Thread t = new Thread(r, Name test);

t.start();

System.out.println(name is: + t.getName());

Thread.currentThread().sleep(5000);

System.out.println(t.isAlive());

System.out.println(over!);

}

}

class MyThread implements Runnable {

public void run() {

for (int i = 0; i < 100; i++)

System.out.println(i);

}

}

该程序的运行结果如下:

name is: Name test

0

1

2

3

. . .

97

98

99

false

over!

3、线程的优先级

(1)优先级(共10级):

它们决定线程执行的先后次序(优先级高者先执行)并可以通过Thread类中的setPriority()和getPriority方法来改变和获取优先级。典型的优先级码

n Thread.MIN_PRIORITY (1级)

n Thread.MAX_PRIORITY(10级)

n Thread.NORM_PRIORITY(5级)

(2)调度规则

Java是不支持线程时间片轮换的调度模型,而采用的是线程优先级高低的抢占调度模型。具有高优先级的线程可以抢占低优先级线程运行的机会。高优先级的线程将始终获得线程执行时间。但是这也不是绝对的,java线程调度器有可能会调用长期处于等待的线程进行执行,所以不要依靠线程的高优先级抢占模型去完成某些功能。

Java线程调度器支持不同优先级线程的抢先方式,但其本身不支持相同优先级线程的时间片轮换。但是如果java运行时系统所在的操作系统(如windows)支持时间片的轮换,则线程调度器就支持相同优先级线程的时间片轮换。

(3)代码示例

package com.px1987.j2se.thread.priority;

public class ThreadPriorityTest {

public static void main(String[] args) {

Thread t1 = new Thread(new MyThread2(), t1);

Thread t2 = new Thread(new MyThread2(), t2);

t1.setPriority(1);

t2.setPriority(10);

t1.start();

t2.start();

}

}

class MyThread2 extends Thread {

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println(Thread.currentThread().getName() + : + i);

yield();

}

}

}

该程序的运行结果如下:

t1: 0

t2: 0

t2: 1

t2: 2

t2: 3

t2: 4

t2: 5

t2: 6

t2: 7

t2: 8

t2: 9

t1: 1

t1: 2

t1: 3

t1: 4

t1: 5

t1: 6

t1: 7

t1: 8

5.java多线程总结 篇五

在多线程程序下,同步能控制对共享资源的访问。如果没有同步,当一个java线程在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误的结果。

1、解释实现多线程的几种方法?

一java线程可以实现runnable接口或者继承thread类来实现,当你打算多重继承时,优先选择实现runnable。

2、thread.start与thread.run()有什么区别?

thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由jvm调度执行run()方法。

3、为什么需要run()和start()方法,我们可以只用run()方法来完成任务吗?

我们需要run()&start()这两个方法是因为jvm创建一个单独的线程不同于普通方法的调用,所以这项工作由线程的start方法来完成,start由本地方法实现,需要显示地被调用,使用这俩个方法的另外一个好处是任何一个对象都可以作为线程运行,只要实现了runnable接口,这就避免因继承了thread类而造成的java的多继承问题。

4、什么是threadlocal类,怎么使用它?

threadlocal是一个线程级别的局部变量,并非“本地线程”。threadlocal为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本(译者注)。

下面是线程局部变量(threadlocal variables)的关键点:

一个线程局部变量(threadlocal variables)为每个线程方便地提供了一个单独的变量。

threadlocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。

当多个线程访问threadlocal实例时,每个线程维护threadlocal提供的独立的变量副本。

常用的使用可在dao模式中见到,当dao类作为一个单例类时,数据库链接(connection)被每一个线程独立的维护,互不影响。(基于线程的单例)

5、什么时候抛出invalidmonitorstateexception异常,为什么?

调用wait()/notify()/notifyall()中的任何一个方法时,如果当前线程没有获得该对象的锁,那么就会抛出illegalmonitorstateexception的异常(也就是说程序在没有执行对象的任何同步块或者同步方法时,仍然尝试调用wait()/notify()/notifyall()时)。由于该异常是runtimeexcpetion的子类,所以该异常不一定要捕获(尽管你可以捕获只要你愿意).作为runtimeexception,此类异常不会在wait(),notify(),notifyall()的方法签名提及。

6、sleep()、suspend()和wait()之间有什么区别?

thread.sleep()使当前线程在指定的时间处于“非运行”(not runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了 interrupt()方法,它将唤醒那个“睡眠的”线程。

注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep(),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。t.suspend()是过时的方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。

object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而不是thread。调用 object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用object.notify(),这样将唤醒原来等待中的线程,然后释放该锁。基本上wait()/notify()与sleep() /interrupt()类似,只是前者需要获取对象锁。

7、在静态方法上使用同步时会发生什么事?

同步静态方法时会获取该类的“class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。

8、当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?

可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,java没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据java照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。

下面有一个示例说明:common类有两个方法synchronizedmethod1()和method1(),mythread类在独立的线程中调用这两个方法。

public class common {

public synchronized void synchronizedmethod1() {

system.out.println(“synchronizedmethod1 called”);

try {

thread.sleep(1000);

} catch (interruptedexception e) {

e.printstacktrace();

}

system.out.println(“synchronizedmethod1 done”);

}

public void method1() {

system.out.println(“method 1 called”);

try {

thread.sleep(1000);

} catch (interruptedexception e) {

e.printstacktrace();

}

system.out.println(“method 1 done”);

}

}

public class mythread extends thread {

private int id = 0;

private common common;

public mythread(string name, int no, common object) {

super(name);

common = object;

id = no;

}

public void run() {

system.out.println(“running thread” + this.getname());

try {

if (id == 0) {

common.synchronizedmethod1();

} else {

common.method1();

}

} catch (exception e) {

e.printstacktrace();

}

}

public static void main(string[] args) {

common c = new common();

mythread t1 = new mythread(“mythread-1”, 0, c);

mythread t2 = new mythread(“mythread-2”, 1, c);

t1.start();

t2.start();

}

}

这里是程序的输出:

running threadmythread-1

synchronizedmethod1 called

running threadmythread-2

method 1 called

synchronizedmethod1 done

method 1 done

结果表明即使synchronizedmethod1()方法执行了,method1()也会被调用。

9、在一个对象上两个线程可以调用两个不同的同步实例方法吗?

不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。看下面代码示例非常清晰:common 类 有synchronizedmethod1()和synchronizedmethod2()方法,mythread调用这两个方法。

public class common {

public synchronized void synchronizedmethod1() {

system.out.println(“synchronizedmethod1 called”);

try {

thread.sleep(1000);

} catch (interruptedexception e) {

e.printstacktrace();

}

system.out.println(“synchronizedmethod1 done”);

}

public synchronized void synchronizedmethod2() {

system.out.println(“synchronizedmethod2 called”);

try {

thread.sleep(1000);

} catch (interruptedexception e) {

e.printstacktrace();

}

system.out.println(“synchronizedmethod2 done”);

}

}

public class mythread extends thread {

private int id = 0;

private common common;

public mythread(string name, int no, common object) {

super(name);

common = object;

id = no;

}

public void run() {

system.out.println(“running thread” + this.getname());

try {

if (id == 0) {

common.synchronizedmethod1();

} else {

common.synchronizedmethod2();

}

} catch (exception e) {

e.printstacktrace();

}

}

public static void main(string[] args) {

common c = new common();

mythread t1 = new mythread(“mythread-1”, 0, c);

mythread t2 = new mythread(“mythread-2”, 1, c);

t1.start();

t2.start();

}

}

10、什么是死锁

死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就javaapi而言,线程死锁可能发生在一下情况。

●当两个线程相互调用thread.join()

●当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

11、什么是线程饿死,什么是活锁?

线程饿死和活锁虽然不想是死锁一样的常见问题,但是对于并发编程的设计者来说就像一次邂逅一样。

当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。javaapi中线程活锁可能发生在以下情形:

●当所有线程在程序中执行object.wait(0),参数为0的wait方法。程序将发生活锁直到在相应的对象上有线程调用object.notify()或者object.notifyall()。

6.创建Java中的线程池Java 篇六

线程是Java的一大特性,它可以是给定的指令序列、给定的方法中定义的变量或者一些共享数据(类一级的变量)。在Java中每个线程有自己的堆栈和程序计数器(PC),其中堆栈是用来跟踪线程的上下文(上下文是当线程执行到某处时,当前的局部变量的值),而程序计数器则用来跟踪当前线程正在执行的指令。

在通常情况下,一个线程不能访问另外一个线程的堆栈变量,而且这个线程必须处于如下状态之一:

1.排队状态(Ready),在用户创建了一个线程以后,这个线程不会立即运行。当线程中的方法start()被调用时,这个线程就会进行排队状态,等待调度程序将它转入运行状态(Running)。当一个进程被执行后它也可以进行排队状态。如果调度程序允许的话,通过调用方法yield()就可以将进程放入排队状态。

2.运行状态(Running),当调度程序将CPU的运行时间分配给一个线程,这个线程就进入了运行状态开始运行。

3.等待状态(Waiting),很多原因都可以导致线程处于等待状态,例如线程执行过程中被暂停,或者是等待I/O请求的完成而进入等待状态。

在Java中不同的线程具有不同的优先级,高优先级的线程可以安排在低优先级线程之前完成。如果多个线程具有相同的优先级,Java会在不同的线程之间切换运行。一个应用程序可以通过使用线程中的方法setPriority()来设置线程的优先级,使用方法getPriority()来获得一个线程的优先级。

线程的生命周期

一个线程的的生命周期可以分成两阶段:生存(Alive)周期和死亡(Dead)周期,其中生存周期又包括运行状态(Running)和等待状态(Waiting)。当创建一个新线程后,这个线程就进入了排队状态(Ready),当线程中的方法start()被调用时,线程就进入生存周期,这时它的方法isAlive()始终返回真值,直至线程进入死亡状态。

线程的实现

有两种方法可以实现线程,一种是扩展java.lang.Thread类,另一种是通过java.lang.Runnable接口。

Thread类封装了线程的行为。要创建一个线程,必须创建一个从Thread类扩展出的新类。由于在Thread类中方法run()没有提供任何的操作,因此,在创建线程时用户必须覆盖方法run()来完成有用的工作。当线程中的方法start()被调用时,方法run()再被调用。下面的代码就是通过扩展Thread类来实现线程:

import java.awt.*;

class Sample1{

public static void main(String[] args){

Mythread test1=new Mythread(1);

Mythread test2=new Mythread(2);

test1.start();

test2.start();

}

}

class Mythread extends Thread {

int id;

Mythread(int i)

{ id=i;}

public void run() {

int i=0;

while(id+i==1){

try {sleep(1000);

} catch(InterruptedException e) {}

}

System.out.println(“The id is ”+id);

}

通常当用户希望一个类能运行在自己的线程中,同时也扩展其它某些类的特性时,就需要借助运行Runnable接口来实现。Runnable接口只有一个方法run()。不论什么时候创建了一个使用Runnable接口的类,都必须在类中编写run()方法来覆盖接口中的run()方法。例如下面的代码就是通过Runnable接口实现的线程:

import java.awt.*;

import java.applet.Applet;

public class Bounce extends Applet implements Runnable{

static int r=30;

static int x=100;

static int y=30;

Thread t;

public void init()

{

t = new Thread(this);

t.start();

}

public void run()

{

int y1=+1;

int i=1;

int sleeptime=10;

while(true)

{

y+=(i*y);

if(y-rgetSize().height)

y1*=-1;

try{

t.sleep(sleeptime);

}catch(InterruptedException e){ }

}}

}

为什么要使用线程池

在Java中,如果每当一个请求到达就创建一个新线程,开销是相当大的,

在实际使用中,每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源,甚至可能要比花在处理实际的用户请求的时间和资源要多得多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个JVM里创建太多的线程,可能会导致系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务,这就是“池化资源”技术产生的原因。

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。另外,通过适当地调整线程池中的线程数目可以防止出现资源不足的情况。

创建一个线程池

一个比较简单的线程池至少应包含线程池管理器、工作线程、任务队列、任务接口等部分。其中线程池管理器(ThreadPool Manager)的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务时进行等待;任务队列的作用是提供一种缓冲机制,将没有处理的任务放在任务队列中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。下面的代码实现了创建一个线程池,以及从线程池中取出线程的操作:

public class ThreadPool

{

private Stack threadpool = new Stack();

private int poolSize;

private int currSize=0;

public void setSize(int n)

{

poolSize = n;

}

public void run()

{

for(int i=0;i

{

WorkThread workthread=new WorkThread();

threadpool.push(workthread);

currSize++;

}}

public synchronized WorkThread getworker( )

{

if (threadpool.empty())

system.out.println(“stack is empty”);

else

try{ return threadpool.pop();

} catch (EmptyStackException e){}

}}

线程池适合应用的场合

当一个Web服务器接受到大量短小线程的请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率。但如果线程要求的运行时间比较长,此时线程的运行时间比创建时间要长得多,单靠减少创建时间对系统效率的提高不明显,此时就不适合应用线程池技术,需要借助其它的技术来提高服务器的服务效率。

7.java多线程总结 篇七

Java编制语言如今已经成为Internet应用的主要开发语言,希望通过此次研究、探讨为未来网络通信的发展提供参考和建议。

1 Java多线程技术

一旦提到Java编程语言的多线程技术,每一位程序设计者、网络设计者必须对多线程有一个深刻的认识。这一类程序执行过程中同一时间可以执行多个指令,这些不同指令之间是相对独立的;即所谓的几种指令流就有几个线程在执行,这种执行方式就定义为线程数。这些线程之间的协调都是由电脑、工作站操作系统来进行独立匹配、调整、控制的。

为了使得在网络通信操作起来更为简单,设计者巧妙地利用多线程技术共享一个存储空间,这与进程有本质的区别,因为进程有自己独立的存储空间,相反,线程却没有。通过将其应用到网络编程中,可以并行发送多个通道执行指令,让网络运行更快、更高效。

2 多线程实现方式与启动

多线程的启动主要有以下几种方法:继承自java.lang.Thread类,重写run方法;扩展java.lang.Runable接口,重写run方法。这两种方法有各自的优缺点,结合自身需求特点选择不同的多线程实现方法。例如,第一种方法定义多线程实现方式如下:

通过第二种方法创建实现Runnable接口的对象不仅可以支持单继承性,而且也不同程度的实现多继承性。以下是一段典型的实例:

3 线程睡眠、优先级、yield、join

在线程网络编程技术中有几个非常重要的概念,这些概念在编程接口和程序执行过程中必须提前设置好。线程睡眠指定的毫秒数,参数是指线程不会运行的最短时间。sleep是Thread类的静态方法,所以一个线程无法使另外一个线程进入睡眠状态,不要使用sleep做定时器。不能通过设置线程的优先级来控制线程的执行先后顺序,Thread类中定义了三个优先级1、5、10。

join,Thread类的非静态方法。如果线程B在线程A完成工作之前不能够进行它的工作,则可以让线程B“加入到”线程A。这也就意味着知道线程A执行完成,线程B不会变为可运行的。

程序在运行过程中有时候容易锁死,这主要是多线程并发执行指令流的时候。为了防止锁死问题,我们采用以下几点来就解决此问题:①所有竞争的资源编序号,按照序号或者优先级来执行需要的指令;②将多个共享资源组成一组放在同一锁下。只有他们同时具备钥匙时候才能顺利执行程序。

4 多线程技术在网络编程中实现建立服务器类

服务器在网络建立过程中非常重要,它起着接受、转换、存储数据的作用。那么,多线程网络编程技术中建立服务器类的过程很重要,以下就详细讲述其建立的过程。在网络交换过程中数据信息处理核心是客户服务器。客户端由一些Java小程序组成,利用这些小程序段实现的主要功能,用户界面设设计、服务器的通信控制,控制这些过程很简单,主要分为两个部分,即图形界面和服务器程序语言与多线程接口的结合,主要程序段如下:

5 结论

通过笔者对Java双线程网络程序编程开发的实例研究学习,该种编程语言设计增强了Java作为网络程序设计语言的优势,为今后应用程序的应用提供理论支持。如今,不管怎么编写程序,满足客户需求这是最基本的要求,网络通信与我们每个人都有着密切关系,网络的快速发展势必要让我们使用更加高端的程序设计机制。我们有必要将其Java多线程网络编程技术进行普及教育,从大学、专科等就让学生有认识。一旦走上编程设计道路,我们应该更加重视它的重要性。为了以后更好的工作生活服务,我们应该加快计算机信息化的普及发展。

参考文献

[1]张卫民.Java语言及其应用[M].北京:清华大学出版社,1996.

[2]金勇华,曲俊生.Java网络高级编程[M].北京:人民邮电出版社,2001.

[3]Eckel B(美).Java编程思想[M].北京:机械工业出版社,1999.

[4]刘巍,唐学兵.利用Java的多线程技术实现数据库的访问[J].计算机应用,2002(12).

8.java多线程总结 篇八

课程名称:专业班级:学生学号:学生姓名:实验名称:实验成绩:课程类别:

验 报 告

Java程序设计

计算机科学与技术2008级

2008081042 王湛泽

JAVA程序设计

必修□限选 公选□ 其它□

实验四

多线程

[实验目的] 1.练习线程的使用以深入理解线程状态与生命周期。2.了解线程调度机制、理解线程同步机制。

[实验内容] 1.编写一个多线程的Java应用程序,经历线程不同的状态与生命周期。

2.编写一个多线程的Java应用程序,在程序中进行线程同步的处理。[实验步骤与要求] 第1题 线程的状态

编写一个Java应用程序,在主线程中再创建2个线程,要求线程经历4种状态:新建、运行、中断和死亡 第2题 排队买票

编写一个Java应用程序,模拟5个人排队买票。售票员只有1张五元的钱,电影票五元一张。假设5个人的名字及排队顺序:赵、钱、孙、李、周。“赵”拿一张二十元的人民币买2张票,“钱”拿1张二十元的人民币1张票,“孙”拿1张十元的人民币买1张票,“李”拿1张十元的人民币买2张票,“周”拿1张五元的人民币买1张票,要求售票员按如下规则找赎:

二十元买2张票,找零:找1张十元;不许找2张五元 二十元买1张票,找零:找1张十元,1张五元;不许找3张五元

十元买1张票,找零:找1张五元 [作业提交] 第一题:

将代码贴在下面:

public class Example8_8{ public static void main(String args[]){ ClassRoom room=new ClassRoom();room.zhangHua.start();

room.teacher.start();} } class ClassRoom implements Runnable{ Thread zhangHua,teacher;ClassRoom(){ teacher=new Thread(this);zhangHua=new Thread(this);zhangHua.setName(“张华”);

teacher.setName(“刘老师”);} public void run(){ Thread thread=Thread.currentThread();if(thread==zhangHua){ try{ System.out.println(thread.getName()+“休息10秒后再说问候”);Thread.sleep(10000);} catch(InterruptedException e){ System.out.println(thread.getName()+“被吵醒了”);} System.out.println(thread.getName()+“说:早上好!”);} else if(thread==teacher){ for(int i=1;i<=2;i++){ System.out.println(thread.getName()+“说:t上课!”);try{ Thread.sleep(500);} 3 catch(InterruptedException e){} }

zhangHua.interrupt();//吵醒zhangXiao } } } 将结果运行截屏贴在下面:

第二题: 代码:

public class Example8_10{ public static void main(String args[]){ String s1=“张”,s2=“钱”,s3=“孙”,s4=“李”,s5=“周”;Cinema canema=new Cinema(s1,s2,s3,s4,s5);Thread zhang,qian,sun,li,zhou;zhang=new Thread(canema);qian=new Thread(canema);sun=new Thread(canema);li=new Thread(canema);zhou=new Thread(canema);zhang.setName(s1);qian.setName(s2);4 sun.setName(s3);li.setName(s4);zhou.setName(s5);zhang.start();qian.start();sun.start();li.start();zhou.start();} } class Cinema implements Runnable{ //实现Runnable接口的类(电影院)TicketSeller seller;//电影院的售票员

String name1,name2,name3,name4,name5;//买票人的名字(线程的名字)Cinema(String s1,String s2,String s3,String s4,String s5){ seller=new TicketSeller();name1=s1;name2=s2;name3=s3;name4=s4;name5=s5;} public void run(){ if(Thread.currentThread().getName().equals(name1)){ seller.sellTicket(20);} else if(Thread.currentThread().getName().equals(name2)){ seller.sellTicket(20);} else if(Thread.currentThread().getName().equals(name3)){ seller.sellTicket(10);} else if(Thread.currentThread().getName().equals(name4)){ seller.sellTicket(10);} else if(Thread.currentThread().getName().equals(name5)){ seller.sellTicket(5);} } } class TicketSeller{ //负责卖票的类

int fiveNumber=1,tenNumber=0,twentyNumber=0;5 public synchronized void sellTicket(int receiveMoney){ String s=Thread.currentThread().getName();if(receiveMoney==5){ fiveNumber=fiveNumber+1;System.out.println(s+“给售票员5元钱,售票员卖给”+s+“一张票,不必找赎”);} else if(receiveMoney==10&&s==“李”){

tenNumber=tenNumber+1;

System.out.println(s+“给售票员10元钱,售票员卖给”+s+“两张票,不必找赎”);

} else if(receiveMoney==10&&s==“孙”){

while(fiveNumber<1){ try{ System.out.println(s+“给售票员10元钱”);System.out.println(“售票员请”+s+“靠边等一会”);wait();//如果线程占有CPU期间执行了wait(),就进入中断状态 System.out.println(s+“结束等待,继续买票”);} catch(InterruptedException e){} } fiveNumber=fiveNumber-1;tenNumber=tenNumber+1;System.out.println(s+“给售票员10元钱,售票员卖给”+s+“一张票,找赎5元”);} else if(receiveMoney==20&&s==“钱”){

while(fiveNumber<1||tenNumber<1){ try{ System.out.println(s+“给售票员20元钱”);System.out.println(“售票员请”+s+“靠边等一会”);wait();//如果线程占有CPU期间执行了wait(),就进入中断状态 System.out.println(s+“结束等待,继续买票”);} catch(InterruptedException e){} } fiveNumber=fiveNumber-1;tenNumber=tenNumber-1;twentyNumber=twentyNumber+1;System.out.println(s+“给售票员20元钱,售票员卖给”+s+“一张票,找赎15 6 元”);} else if(receiveMoney==20&&s==“赵”){

while(tenNumber<1){ try{ System.out.println(s+“给售票员20元钱”);System.out.println(“售票员请”+s+“靠边等一会”);wait();//如果线程占有CPU期间执行了wait(),就进入中断状态 System.out.println(s+“结束等待,继续买票”);} catch(InterruptedException e){} }

tenNumber=tenNumber-1;twentyNumber=twentyNumber+1;System.out.println(s+“给售票员20元钱,售票员卖给”+s+“两张票,找赎15元”);}

截屏:

9.IOS开发――多线程编程 篇九

2.在众手机商拼CPU主频,拼4核,8核的年代,苹果依然坚持双核,iphone用户体验仍然坚挺。

以上两点IOS是如何优化,在续航,流畅度和响应速度上完胜安卓,答案就是多线程&RunLoop...

RunLoop是IOS事件响应与任务处理最核心机制,它贯穿IOS整个系统运作,

RunLoop不像一般的线程循环等待任务,传统的线程循环等待任务会导致CPU时间被占用,虽然你设置了睡眠时间,但很多时候会出现空转,

而RunLoop是监控事件触发处理机制,说白了,在有事件的时候CPU全力生产,当没有事件产生的时候,线程就挂起等待事件。

10.java多线程总结 篇十

然而, 不管是JDK 1.0的Vector和Hashtable, 还是它们之后的集合基类和同步包装器, 仍不足以提供真正的线程安全性, 许多公用的混合操作仍然需要额外的同步因为单个方法同步了并不代表混合的方法调用具有原子性 (atomic) 。对于混合操作, 集合仍然需要进行额外的同步。请看下面的代码片断:

这是经典的put-if-absent问题, 尽管contains, add方法都同步了, 但作为vector之外的使用环境, 仍然存在竞争条件:虽然if (!vector.contains (element) ) 与方法调用vector.add (element) 都是原子性的操作, 但在if条件判断为真后, vector.contains方法的锁已经释放, 那么在即将调用vector.add方法之间存在间隙, 在多线程环境下, 完全有可能被其他线程获得vector的锁并改变其状态, 此时当前线程的vector.add (element) 正在等待, 只有当其他线程释放了锁后, vector.add (element) 才执行, 但此时它已经基于一个错误的前提了。所以, 在多线程环境下, 我们仍然需要锁住整个Vector。

J2SE 1.5引入了java.util.concurrent (juc) 包, 其中包含了一个小的基于Abstract Queued Synchronizer的同步框架, 大部分juc内的同步器 (Synchronizers) 都是基于该框架实现的[1]。这个包涵盖了并发集合类、线程池机制、同步互斥机制、线程安全的变量更新工具类、锁等常用工具。它们的引入大大简化了多线程程序的开发。以并发集合类为例, 有Concurrent Hash Map、Concurrent Linked Queue、Copy On Write Array List等等, 我们可以根据不同的使用场景, 用它们替换java.util包中的相应集合类, 完全无需考虑并发问题。

java.util.Concurrent Modification Exception是对集合进行迭代的过程中常常遇到的出错提示, 接下来本文从该异常出发, 分析它发生的根本原因和底层机理, 给出在多线程环境下使用Java集合类的正确方法。

1. 快速失败 (fail-fast) 迭代器

当使用迭代器 (Iterator) 对Java集合对象进行迭代时, 如果针对该集合同时实施了任何添加或删除一个或多个元素的操作, 或者显式地调整底层数组的大小, 迭代器会立即抛出java.util.Concurrent Modification Exception。

例如, 下面的代码创建了一个包含五个键值对的Hash Map, 希望除去其中的第三个键值对:

这段代码在迭代循环中移除了一个键值对, 导致集合的大小发生了改变, 迭代器会尽快抛出Concurrent Modification Exception, 而不会冒着迭代的结果是不明确的风险, 这一特征被称为“快速失败 (fail-fast) 的”, 这意味着如果使用快速失败迭代器在collection上迭代时直接改变该collection而导致大小发生变化时, 迭代器将抛出此异常。

究其原因, 这是因为Iterator是工作在一个独立的线程中, 并且拥有一个锁。Iterator被创建之后会建立一个指向原来对象的单链索引表, 当原来的对象数量发生变化时, 这个索引表的内容不会同步改变, 所以当索引指针往后移动的时候就找不到要迭代的对象, 按照fail-fast原则Iterator会马上抛出异常。

Concurrent Modification Exception不管是在单线程环境中还是在多线程环境下都会发生。要有效地避免这一错误, 可以使用迭代器本身的remove () , add () 方法。将上面的代码稍作修改, 就可以解决这个问题, 如下所示:

不过, 使用这种方法只能从集合中移除同一个元素而不能移除别的元素, 即, 在上例中, 当找到"key3"键时可以通过it.remove () ;移除"key3-value3"键值对而不能移除别的键值对。

2. 在多线程环境下避免Concurrent Modification E-xception的方法

总的来说, 在多线程环境下避免Concurrent Modification Exception的方法可以分为两种:第一种是用数组代替迭代器;第二种是使用并发集合类。下面分别进行阐述。

2.1 用数组代替迭代器

这种方法避免使用迭代器, 而是转而使用数组, 然后遍历数组, 如下所示:

这里的关键是my Map.key Set () .to Array () ;新分配了一个包含所有Key的数组, 该数组是安全的, 因为它与原来的集合之间没有维持任何对象的引用[2]。

使用数组的方法非常适用于中小规模集合, 但如果集合很大, 会导致性能明显下降。

2.2 使用并发集合类

从JDK 1.5开始, Java增加了java.util.concurrent包, 内容涵盖了并发集合类[3]、线程池机制、同步互斥机制、线程安全的变量更新工具类、锁等常用工具。它们的引入大大简化了多线程程序的开发。以并发集合类为例, 引入了C o n c u r r e n t H a s h M a p、Concurrent Linked Queue、Copy On Write Array List等等, 在多线程环境下我们可以用它们替换java.util包中的相应集合类, 完全无需考虑并发问题。下面的代码演示了如何使用Copy On Write Array List和Concurrent Hash Map。

例1首先获取数组元素的迭代器, 在迭代过程中当找到2时将3移除, 并添加另两个值5和6。运行结果是:

例2首先获取键的迭代器, 在迭代过程中当找到key等于key1的键时将key3-value3键值对移除, 并添加另两对键值对key4-value4和key5-value5。运行结果是:

上面的例子明确地显示, 使用并发集合类避免了Concurrent Modification Exception的发生。

3. 结束语

线程安全性是多线程环境下的编程必须面对的棘手的问题。本文分析了java.util.Concurrent Modification Exception异常发生的根本原因和底层机理, 给出在多线程环境下正确使用Java集合类的两个方法, 一个是将迭代器转换为数组, 另一个是使用并发集合类。掌握了这两种方法, 才能在多线程环境下正确地使用Java集合类。

摘要:线程安全性是多线程环境下的编程必须面对的棘手的问题。本文从对集合进行迭代常常遇到的java.util.Concurrent Modification Exception出发, 分析了异常发生的根本原因和底层机理, 给出在多线程环境下使用Java集合类的两个正确方法, 一个是将迭代器转换为数组, 另一个是使用并发集合类。掌握了这两种方法, 才能在多线程环境下正确地使用Java集合类。

关键词:Java集合,Collection,多线程,并发集合类

参考文献

[1]Doug Lea.The java.util.concurrent synchronizer framework[J].Science of Computer Programming-Special issue:Concurrency and synchronization in Java programs archive, Volume58Issue3, Dec2005:293-309.

[2]Oracle, Java Platform Standard Ed.6[EB], http://docs.oracle.com/javase/6/docs/api/java/util/Set.html#toArray () .

上一篇:科技知识答案下一篇:最令我难忘的人记叙文作文