多线程编程
来源:互联网 发布:淘宝怎么修改好评评价 编辑:程序博客网 时间:2024/06/07 20:45
多线程编程
Thread类关键技术点:
线程的启动如何使线程暂停如何使线程停止线程的优先级线程安全相关的问题
1.main线程和main方法没有任何关系,仅仅是名字相同;
2.进程的概念,联想任务管理器中的进程,就是一个个exe应用程序;
3.什么是多线程?就是边吃饭边放屁,边听音乐边打字;
4.什么是同步?就是单纯的排队,一个执行完再执行下一个;
5.什么是异步?就是快速地轮流执行,联系硬件中的流水灯,处理器的来回执行速度远远大于人们的反应速度。异步就是多线程;
6.多线程的实现。两种方法,一种是继承Thread类,另一种是实现Runnable接口。其实Thread类也是实现了Runnable接口。区别:类只能单根继承,接口可以实现多继承;
7.使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。下面举例:
代码:
package test;public class MyThread extends Thread{ public void run(){ super.run(); System.out.println("MyThread"); }}
代码:
package test;public class Run { public static void main(String[] args){ MyThread mythread = new MyThread(); mythread.start(); System.out.println("运行结束!"); }}
结果截图:
如图,MyThread.java类中的run方法执行的时间比较晚。
8.线程的随机性
代码:
package test;public class MyThread extends Thread{ public void run(){ try{ for(int i=0;i<10;i++ ){ int time =(int)( Math.random() * 1000 ); Thread.sleep(time); System.out.println( "run=" + Thread.currentThread().getName() ); } }catch (InterruptedException e){ e.printStackTrace(); } }}
代码:
package test;public class Test { public static void main( String[] args ){ try{ MyThread thread = new MyThread(); thread.setName("myThread"); thread.start(); for( int i=0;i<10;i++ ){ int time =(int) (Math.random() * 1000); Thread.sleep(time); System.out.println("main="+Thread.currentThread().getName()); } }catch (InterruptedException e){ e.printStackTrace(); } }}
运行结果:
总结,线程具有随机的特性。start方法通知“线程规划器”线程已经准备就绪,等待调用线程对象的run方法。启动线程具有异步执行的效果。
9.执行start方法的顺序不代表线程启动的顺序,举例:
代码:
package test;public class MyThread extends Thread{ private int i; public MyThread(int i){ super(); this.i = i; } public void run(){ System.out.println(i); }}
代码:
package test;public class Test{ public static void main( String[] args ){ MyThread t11 = new MyThread(1); MyThread t12 = new MyThread(2); MyThread t13 = new MyThread(3); MyThread t14 = new MyThread(4); MyThread t15 = new MyThread(5); MyThread t16 = new MyThread(6); MyThread t17 = new MyThread(7); MyThread t18 = new MyThread(8); MyThread t19 = new MyThread(9); MyThread t20 = new MyThread(10); t11.start(); t12.start(); t13.start(); t14.start(); t15.start(); t16.start(); t17.start(); t18.start(); t19.start(); t20.start(); }}
结果:
10.实现Runnable接口,如果欲创建的线程类已经有一个父类了,这时就不能再继承自Thread类,因为不支持多继承,所以就需要实现Runnable接口来应对这样的情况。
举例:
构造函数支持传入一个Runnable接口的对象。
代码:
package myrunnable;public class MyRunnable implements Runnable { public void run() { System.out.println("运行中!"); }}
package test;import myrunnable.MyRunnable;public class Test{ public static void main(String[] args){ Runnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); System.out.println("运行结束!"); }}
而且,因为Thread.java类也实现了Runnable接口,所以,构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run方法交由其他的线程进行调用。
11.实例变量和线程安全
数据不共享的情况:
代码:
package test;public class MyThread extends Thread{ private int count = 5; public MyThread(String name){ super(); this.setName(name); } public void run(){ super.run(); while(count>0){ count--; System.out.println( "由 "+ this.currentThread().getName() + " 计算,count="+count ); } }}
package test;public class Run { public static void main(String[] args){ MyThread a = new MyThread("A"); MyThread b = new MyThread("B"); MyThread c = new MyThread("C"); a.start(); b.start(); c.start(); }}
结果:
可以看到,一共创建了3个线程,每个线程都有各自的count变量,自己减少自己的count变量的值。这样的情况就是变量不共享。
12.变量共享、共享数据的情况:
共享数据的情况就是多个线程可以访问同一个变量,比如在实现投票功能的软件时,多个线程可以同时处理同一个人的票数。
代码:
package test;public class MyThread extends Thread{ private int count =5 ; public void run(){ super.run(); count--; //此示例不要用for语句,因为使用同步后其他线程就得不到运行的机会了 //一直由一个线程进行减法运算 System.out.println( "由 "+this.currentThread().getName() +" 计算,count=" +count ); }}
package test;public class Run { public static void main(String[] args){ MyThread mythread = new MyThread(); Thread a = new Thread(mythread,"A"); Thread b = new Thread(mythread,"B"); Thread c = new Thread(mythread,"C"); Thread d = new Thread(mythread,"D"); Thread e = new Thread(mythread,"E"); a.start(); b.start(); c.start(); d.start(); e.start(); }}
运行结果:
从结果可以看出,线程A和线程B打印出的count值都是3,说明A和B同时对count进行处理,产生了“非线程安全” 问题:我们想要的打印结果不是重复的,是依次递减的。
比如在某些JVM中,i–的操作分成了3步:
1.取得原有i值;
2.计算i-1;
3.对i进行赋值。
如果在进行3个步骤的过程中,有多个线程同时访问,那么一定会出现非线程安全问题。
解决方法是在MyThread类中的run方法用synchronized修饰符进行修饰。
通过在run方法前加入synchronized关键字,使多个线程在执行run方法时,以排队的方式进行处理。当一个线程调用run前,先判断run方法有没有被上锁,如果上锁,说明有其他的线程正在调用run方法,必须等其他线程对run方法调用结束后才可以执行run方法。这样就实现了排队调用run方法的目的,也就达到了按顺序对count变量减1的效果。
synchronized可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
当一个线程想要执行同步方法里面的代码是,线程首先尝试去拿这把锁,如果能够拿到这把锁,那么这个线程就可以执行synchronized里面的代码,如果不能拿到这把锁,那么这个线程就会不断尝试拿这把锁,直到能够拿到为止,而且是多个线程同时去争抢这把锁。
13.非线程安全问题举例
代码:
package controller;//本类模拟成一个Servlet组件public class LoginServlet { private static String usernameRef; private static String passwordRef; public static void doPost(String username,String password){ try { usernameRef = username; if ( username.equals("a")){ Thread.sleep(5000); } passwordRef = password; System.out.println( "username= "+usernameRef +" password="+passwordRef ); }catch (InterruptedException e){ e.printStackTrace(); } }}
package extthread;import controller.LoginServlet;public class ALogin extends Thread{ public void run(){ LoginServlet.doPost("a","aa" ); }}
package extthread;import controller.LoginServlet;public class BLogin extends Thread { public void run(){ LoginServlet.doPost("b","bb"); }}
package test;import extthread.ALogin;import extthread.BLogin;public class Run { public static void main(String[] args){ ALogin a= new ALogin(); a.start(); BLogin b = new BLogin(); b.start(); }}
运行结果:
14.留意i–和System.out.println()的异常
package test;public class MyThread extends Thread{ private int i =5; public void run(){ System.out.println( "i="+(i--) + " threadName=" +Thread.currentThread().getName()); }}
package test;import extthread.ALogin;import extthread.BLogin;public class Run { public static void main(String[] args){ MyThread run = new MyThread(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); Thread t4 = new Thread(run); Thread t5 = new Thread(run); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); }}
运行结果:
上面的实验说明:虽然println方法在内部是同步的,但是i–的操作却是在进入println方法之前发生的,所以有发生非线程安全问题的概率。
所以,为了防止发生非线程安全问题,还是应继续使用同步方法。
public void println(String x){ synchronized(this){ print(x); newLine(); }}
15.currentThread方法
currentThread方法可以返回代码段正在被哪个线程调用的信息。
举例:
package test;public class Run { public static void main(String[] args){ System.out.println(Thread.currentThread().getName()); }}
运行结果:
结果说明,main方法被名为main的线程调用。
package test;public class MyThread extends Thread{ public MyThread(){ System.out.println("构造方法的打印: "+Thread.currentThread().getName() ); } public void run(){ System.out.println( "run方法的打印: "+Thread.currentThread().getName() ); }}
package test;public class Run { public static void main(String[] args){ MyThread mythread = new MyThread(); mythread.start(); //mythread.run(); }}
运行结果:
package test;public class Run { public static void main(String[] args){ MyThread mythread = new MyThread(); //mythread.start(); mythread.run(); }}
16.isAlive方法
isAlive方法的功能是判断当前的线程是否处于活动状态。
package test;public class MyThread extends Thread{ public void run(){ System.out.println( "run=: "+ this.isAlive() ); }}
package test;public class Run { public static void main(String[] args){ MyThread mythread = new MyThread(); System.out.println( "begin ==" + mythread.isAlive() ); mythread.start(); System.out.println( "end ==" + mythread.isAlive()); }}
运行结果:
方法isAlive的作用是测试线程是否处于活动状态?
什么是活动状态呢?
活动状态就是线程已经启动 且尚未终止。线程处于正在运行或真被开始运行的状态,就认为线程是“存活”的。
在使用isAlive方法时,如果将线程对象以构造参数的方式传递给Thread对象进行start启动时,运行的结果和前面的实例是有差异的。
造成差异的原因是来自于Thread.currentThread()和this的差异。
17.sleep方法
方法sleep的作用是在指定的毫秒数内让当前的“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。
package test;public class MyThread extends Thread{ public void run(){ try { System.out.println("run threadName =" +this.currentThread().getName() + " begin"); Thread.sleep(2000); System.out.println( "run threadName =" +this.currentThread().getName()+" end"); }catch(InterruptedException e){ e.printStackTrace(); } }}
package test;public class Run { public static void main(String[] args){ MyThread mythread = new MyThread(); System.out.println( "begin ==" + System.currentTimeMillis() ); mythread.run(); System.out.println( "end ==" + System.currentTimeMillis()); }}
运行结果:
而如果将mythread中的run方法改为调用start方法:
代码:
package test;public class Run { public static void main(String[] args){ MyThread mythread = new MyThread(); System.out.println( "begin ==" + System.currentTimeMillis() ); mythread.start(); System.out.println( "end ==" + System.currentTimeMillis()); }}
运行结果:
第二版本中,因为main线程和mythread线程是异步执行的,所以首先打印的信息是begin和end.
18.getId方法
package test;public class Test{ public static void main(String[] args){ Thread runThread = Thread.currentThread(); System.out.println( runThread.getName() + " " +runThread.getId() ); }}
19.停止线程
停止线程在java语言汇总并不像break语句那样干脆,需要一些技巧性的处理。
使用java内置支持多线程的类设计多线程应用是很常见的事情,然而,多线程给开发人员带来了一些新的挑战,如果处理不好就狐疑导致超出预期的行为并且难以定位错误。
本节应该实现更好地停止一个线程。停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。虽然这看起来非常简单,但是必须做好防范措施,以便达到预期的效果。停止一个线程可以使用Thread.stop()方法,但最好不用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是已被弃用作废的,在将来的java版本中,这个方法将不可用或不被支持。
大多数停止一个线程的操作使用Thread.interrupt()方法,尽管方法的名称是“停止,中止”的意思,但是这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。
在java中有以下3种方法可以终止正在运行的线程:
1.使用退出标志,使线程正常退出,也就是当run方法完成后线程终止;2.使用stop方法强行终止线程,但是不推荐使用,因为stop和suspend及resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果;3.使用interrupt方法中断线程。
20.停止不了的线程
interrupt方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。
21.
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- 多线程编程
- Django源码分析3:处理请求wsgi分析与视图View
- JetClean绿色破解版下载及软件破解教程
- 观察者模式和监听模式
- Calendar 类
- java基础语法
- 多线程编程
- 求一个数到另一个数累加之和
- 介绍了webkit到webengine的和webengine中js和C++互相调用的方法
- centos7 git更新
- 8.1.1分析程序
- WebService的整理:
- zookeeper-简介(二)
- Web服务器和客户端
- static用法