志在指尖
用双手敲打未来

java多线程编程(实例代码)

java多线程编程

一、多线程的优缺点
多线程的优点:
1)资源利用率更好
2)程序设计在某些情况下更简略
3)程序呼应更快
多线程的代价:
1)设计更杂乱
虽然有一些多线程应用程序比单线程的应用程序要简略,但其他的一般都更杂乱。在多线程拜访同享数据的时分,这部分代码需求特别的留意。线程之间的交互往往十分杂乱。不正确的线程同步产生的过错十分难以被发现,而且重现以修正。
2)上下文切换的开销
当CPU从履行一个线程切换到履行别的一个线程的时分,它需求先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开端履行。这种切换称为“上下文切换”(“contextswitch”)。CPU会在一个上下文中履行一个线程,然后切换到别的一个上下文中履行别的一个线程。上下文切换并不廉价。假如没有必要,应该减少上下文切换的产生。java多线程编程
二、创立java多线程
1、创立Thread的子类
创立Thread子类的一个实例并重写run办法,run办法会在调用start()办法之后被履行。比如如下:
publicclassMyThreadextendsThread{
publicvoidrun(){
System.out.println(“MyThreadrunning”);
}
}
MyThreadmyThread=newMyThread();
myTread.start();
也可以如下创立一个Thread的匿名子类:
Threadthread=newThread(){
publicvoidrun(){
System.out.println(“ThreadRunning”);
}
};
thread.start();
2、完结Runnable接口
第二种编写线程履行代码的方式是新建一个完结了java.lang.Runnable接口的类的实例,实例中的办法可以被线程调用。下面给出比如:
publicclassMyRunnableimplementsRunnable{
publicvoidrun(){
System.out.println(“MyRunnablerunning”);
}
}
Threadthread=newThread(newMyRunnable());
thread.start();
同样,也可以创立一个完结了Runnable接口的匿名类,如下所示:
RunnablemyRunnable=newRunnable(){
publicvoidrun(){
System.out.println(“Runnablerunning”);
}
}
Threadthread=newThread(myRunnable);
thread.start();
三、线程安全
在同一程序中运转多个线程自身不会导致问题,问题在于多个线程拜访了相同的资源。如同一内存区(变量,数组,或目标)、系统(数据库,webservices等)或文件。实际上,这些问题只要在一或多个线程向这些资源做了写操作时才有可能产生,只需资源没有产生变化,多个线程读取相同的资源便是安全的。
当两个线程竞赛同一资源时,假如对资源的拜访顺序敏感,就称存在竞态条件。导致竞态条件产生的代码区称作临界区。
假如一个资源的创立,运用,销毁都在同一个线程内完结,且永远不会脱离该线程的操控,则该资源的运用便是线程安全的。
四、java同步块
Java中的同步块用synchronized符号。同步块在Java中是同步在某个目标上。一切同步在一个目标上的同步块在一起只能被一个线程进入并履行操作。一切其他等候进入该同步块的线程将被堵塞,直到履行该同步块中的线程退出。
有四种不同的同步块:
实例办法
静态办法
实例办法中的同步块
静态办法中的同步块
实例办法同步:
publicsynchronizedvoidadd(intvalue){
this.count+=value;
}
Java实例办法同步是同步在具有该办法的目标上。这样,每个实例其办法同步都同步在不同的目标上,即该办法所属的实例。只要一个线程可以在实例办法同步块中运转。假如有多个实例存在,那么一个线程一次可以在一个实例同步块中履行操作。一个实例一个线程。
静态办法同步:
publicstaticsynchronizedvoidadd(intvalue){
count+=value;
}
静态办法的同步是指同步在该办法地点的类目标上。由于在Java虚拟机中一个类只能对应一个类目标,所以一起只答应一个线程履行同一个类中的静态同步办法。
实例办法中的同步块:
publicvoidadd(intvalue){
synchronized(this){
this.count+=value;
}
}
留意Java同步块构造器用括号将目标括起来。在上例中,运用了“this”,即为调用add办法的实例自身。在同步构造器顶用括号括起来的目标叫做监视器目标。上述代码运用监视器目标同步,同步实例办法运用调用办法自身的实例作为监视器目标。一次只要一个线程可以在同步于同一个监视器目标的Java办法内履行。
下面两个比如都同步他们所调用的实例目标上,因此他们在同步的履行效果上是等效的。
publicclassMyClass{
publicsynchronizedvoidlog1(Stringmsg1,Stringmsg2){
log.writeln(msg1);
log.writeln(msg2);
}
publicvoidlog2(Stringmsg1,Stringmsg2){
synchronized(this){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
静态办法中的同步块:
publicclassMyClass{
publicstaticsynchronizedvoidlog1(Stringmsg1,Stringmsg2){
log.writeln(msg1);
log.writeln(msg2);
}
publicstaticvoidlog2(Stringmsg1,Stringmsg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
这两个办法不答应一起被线程拜访。假如第二个同步块不是同步在MyClass.class这个目标上。那么这两个办法可以一起被线程拜访。
五、java线程通讯
线程通讯的目标是使线程间可以互相发送信号。另一方面,线程通讯使线程可以等候其他线程的信号。
Java有一个内建的等候机制来答应线程在等候信号的时分变为非运转状况。java.lang.Object类定义了三个办法,wait()、notify()和notifyAll()来完结这个等候机制。
一个线程一旦调用了恣意目标的wait()办法,就会变为非运转状况,直到另一个线程调用了同一个目标的notify()办法。为了调用wait()或许notify(),线程有必要先取得那个目标的锁。也便是说,线程有必要在同步块里调用wait()或许notify()。
以下为一个运用了wait()和notify()完结的线程间通讯的同享目标:
仿制代码
publicclassMyWaitNotify{
MonitorObjectmyMonitorObject=newMonitorObject();
booleanwasSignalled=false;
publicvoiddoWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
}catch(InterruptedExceptione){…}
}
//clearsignalandcontinuerunning.
wasSignalled=false;
}
}
publicvoiddoNotify(){
synchronized(myMonitorObject){
wasSignalled=true;
myMonitorObject.notify();
}
}
}
留意以下几点:
1、不管是等候线程仍是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程假如没有持有目标锁,将不能调用wait(),notify()或许notifyAll()。不然,会抛出IllegalMonitorStateException反常。
2、一旦线程调用了wait()办法,它就释放了所持有的监视器目标上的锁。这将答应其他线程也可以调用wait()或许notify()。
3、为了防止丢失信号,有必要把它们保存在信号类里。如上面的wasSignalled变量。
4、假唤醒:由于不可思议的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这便是所谓的假唤醒(spuriouswakeups)。为了防止假唤醒,保存信号的成员变量将在一个while循环里承受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁。
5、不要在字符串常量或大局目标中调用wait()。即上面MonitorObject不能是字符串常量或是大局目标。每一个MyWaitNotify的实例都具有一个属于自己的监视器目标,而不是在空字符串上调用wait()/notify()。
六、java中的锁
自Java5开端,java.util.concurrent.locks包中包含了一些锁的完结,因此你不用去完结自己的锁了。
常用的一些锁:
java.util.concurrent.locks.Lock;
java.util.concurrent.locks.ReentrantLock;
java.util.concurrent.locks.ReadWriteLock;
java.util.concurrent.locks.ReentrantReadWriteLock;
一个可重入锁(reentrantlock)的简略完结:
publicclassLock{
booleanisLocked=false;
ThreadlockedBy=null;
intlockedCount=0;
publicsynchronizedvoidlock()throwsInterruptedException{
ThreadcallingThread=Thread.currentThread();
while(isLocked&&lockedBy!=callingThread){
wait();
}
isLocked=true;
lockedCount++;
lockedBy=callingThread;
}
publicsynchronizedvoidunlock(){
if(Thread.currentThread()==this.lockedBy){
lockedCount–;
if(lockedCount==0){
isLocked=false;
notify();
}
}
}
}
留意的一点:在finally语句中调用unlock()
lock.lock();
try{
//docriticalsectioncode,whichmaythrowexception
}finally{
lock.unlock();
}
七、java中其他同步办法
信号量(Semaphore):java.util.concurrent.Semaphore
堵塞队列(BlockingQueue):java.util.concurrent.BlockingQueue
publicclassBlockingQueue{
privateListqueue=newLinkedList();
privateintlimit=10;
publicBlockingQueue(intlimit){
this.limit=limit;
}
publicsynchronizedvoidenqueue(Objectitem)throwsInterruptedException{
while(this.queue.size()==this.limit){
wait();
}
if(this.queue.size()==0){
notifyAll();
}
this.queue.add(item);
}
publicsynchronizedObjectdequeue()throwsInterruptedException{
while(this.queue.size()==0){
wait();
}
if(this.queue.size()==this.limit){
notifyAll();
}
returnthis.queue.remove(0);
}
}
八、java中的线程池
Java经过Executors供给四种线程池,分别为:
newCachedThreadPool
创立一个可缓存的线程池。假如线程池的巨细超过了处理使命所需求的线程,那么就会收回部分空闲(60秒不履行使命)的线程,当使命数增加时,此线程池又可以智能的增加新线程来处理使命。此线程池不会对线程池巨细做约束,线程池巨细完全依赖于操作系统(或许说JVM)可以创立的最大线程巨细。
newFixedThreadPool
创立固定巨细的线程池。每次提交一个使命就创立一个线程,直到线程到达线程池的最大巨细。线程池的巨细一旦到达最大值就会保持不变,假如某个线程由于履行反常而完毕,那么线程池会补充一个新线程。
newScheduledThreadPool
创立一个巨细无约束的线程池。此线程池支持守时以及周期性履行使命。
newSingleThreadExecutor
创立一个单线程的线程池。此线程池支持守时以及周期性履行使命。这个线程池只要一个线程在作业,也便是相当于单线程串行履行一切使命。假如这个唯一的线程由于反常完毕,那么会有一个新的线程来代替它。此线程池确保一切使命的履行顺序依照使命的提交顺序履行。
线程池简略用法:
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
publicclassMain{
publicstaticvoidmain(String[]args){
ExecutorServicecachedThreadPool=Executors.newCachedThreadPool();
for(inti=0;i<10;i++){
finalintindex=i;
cachedThreadPool.execute(newRunnable(){
publicvoidrun(){
System.out.println(index);
}
});
}
}
}

java多线程编程实例代码

java中可有两种办法完成多线程:
一种是承继Thread类;
一种是完成Runnable接口;
Thread类
是在java.lang包中界说的。一个类只要承继了Thread类同时覆写了本类中的run()办法就能够完成多线程操作了,但限制是一个类只能承继一个父类
packageorg.thread.demo;classMyThreadextendsThread{privateStringname;publicMyThread(Stringname){super();this.name=name;
}publicvoidrun(){for(inti=0;i<10;i++){
System.out.println(“线程开始:”+this.name+”,i=”+i);
}
}
}packageorg.thread.demo;publicclassThreadDemo01{publicstaticvoidmain(String[]args){
MyThreadmt1=newMyThread(“线程a”);
MyThreadmt2=newMyThread(“线程b”);
mt1.run();
mt2.run();
}
}
但这种调用是顺序履行的,先履行mt1,然后mt2。没有并行。并行需要调用start()办法,则会经过JVM找到run()办法,在jdk中能够看源码
packageorg.thread.demo;publicclassThreadDemo01{publicstaticvoidmain(String[]args){
MyThreadmt1=newMyThread(“线程a”);
MyThreadmt2=newMyThread(“线程b”);
mt1.start();
mt2.start();
}
};
start()—用于发动线程。
使用了privatenativevoidstart();其中native关键字表明能够调用操作系统的底层函数,那么这样的技能成为JNI技能(javaNativeInterface)
但实际开发多线程操作很少用Thread类,而是经过Runnable接口完成。
publicinterfaceRunnable{
publicvoidrun();
}
例子:
packageorg.runnable.demo;classMyThreadimplementsRunnable{privateStringname;publicMyThread(Stringname){this.name=name;
}publicvoidrun(){for(inti=0;i<100;i++){
System.out.println(“线程开始:”+this.name+”,i=”+i);
}
}
};
上例或许直接,实例化借口,重载run函数:packageorg.runnable.demo;importorg.runnable.demo.MyThread;publicclassThreadDemo01{publicstaticvoidmain(String[]args){
MyThreadmt1=newMyThread(“线程a”);
MyThreadmt2=newMyThread(“线程b”);newThread(mt1).start();newThread(mt2).start();
}
}
Runnablerun=newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<10;i++){
System.out.println(i);
}
}
};
调用经过
Threadthread=newThread(run);
thread.start();
用Runnable界说的子类中没有start()办法。所以需要经过Thread类来发动Runnable完成的多线程,也就是说用start()才干协调系统的资源,
Thread构造办法:publicThread(Runnabletarger)。
Runnable接口比较承继Thread类有如下好处:
避免点承继的限制,一个类能够承继多个接口。
适合于资源的同享
而承继Thread不能直接进行多线程作业,你得为你的每一部分作业都界说一个线程。
比如买票实例,使用Thread就没有办法完成多线程同享票务资源,每个Thread类拥有自己独立的占有资源。使用Runnable能够同享资源,一起同享总票务资源,如:
classMyThreadimplementsRunnable{privateintticket=10;publicvoidrun(){for(inti=0;i<20;i++){if(this.ticket>0)
System.out.println(“卖票:ticket”+this.ticket–);
}
}
};
MyThreadmt=newMyThread();newThread(mt).start();//同一个mt,但是在Thread中就不能够,如果用同一newThread(mt).start();//个实例化目标mt,就会出现异常newThread(mt).start();

未经允许不得转载:IT技术网站 » java多线程编程(实例代码)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载