网站做第三方登录,品牌型网站建设的好处,搜索引擎排名大全,行业网站建设哪家好昨天熬了个通宵#xff0c;看了一晚上的视频#xff0c;把java 的多线程相关技术重新复习了一遍#xff0c;下面对学习过程中遇到的知识点进行下总结。首先我们先来了解一下进程、线程、并发执行的概念#xff1a;进程是指#xff1a;一个内存中运行的应用程序#xff0c…昨天熬了个通宵看了一晚上的视频把java 的多线程相关技术重新复习了一遍下面对学习过程中遇到的知识点进行下总结。首先我们先来了解一下进程、线程、并发执行的概念进程是指一个内存中运行的应用程序每个进程都有自己独立的一块内存空间一个进程中可以启动多个线程。比如在Windows系统中一个运行的exe就是一个进程。线程是指进程中的一个执行流程一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程进程中的多个线程共享进程的内存。一般来说当运行一个应用程序的时候就启动了一个进程当然有些会启动多个进程。启动进程的时候操作系统会为进程分配资源其中最主要的资源是内存空间因为程序是在内存中运行的。在进程中有些程序流程块是可以乱序执行的并且这个代码块可以同时被多次执行。实际上这样的代码块就是线程体。线程是进程中乱序执行的代码流程。当多个线程同时运行的时候这样的执行模式成为并发执行。线程的状态1、线程共有下面4种状态新建状态(New)新创建了一个线程对象当你用new创建一个线程时该线程尚未运行。就绪状态(Runnable)线程对象创建后其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中变得可运行等待获取CPU的使用权。运行状态(Running)就绪状态的线程获取了CPU执行程序代码。阻塞状态(Blocked)阻塞状态是线程因为某种原因放弃CPU使用权暂时停止运行。直到线程进入就绪状态才有机会转到运行状态。阻塞的情况分三种a. 等待阻塞运行的线程执行wait()方法JVM会把该线程放入等待池中。b. 同步阻塞运行的线程在获取对象的同步锁时若该同步锁被别的线程占用则JVM把该线程放入锁。c. 其他阻塞运行的线程执行sleep()或join()方法或者发出了I/O请求时JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时线程重新转入就绪状态。死亡状态(Dead)a. 由于run方法的正常退出而自然死亡;b. 没有捕获到的异常事件终止了run方法的执行从而导致线程突然死亡2、若要确定某个线程当前是否活着可以使用isAlive方法。如果该线程是可运行线程或者被中断线程那么该方法返回true如果该线程仍然是个新建线程或者该线程是个死线程那么该方法返回false3、注意你无法确定一个活线程究竟是处于可运行状态还是被中断状态也无法确定一个可运行线程是否正处在运行之中。另外你也无法对尚未成为可运行的线程与已经死掉的线程进行区分。4、线程必须退出中断状态并且返回到可运行状态方法是使用与进入中断状态相反的过程a.如果线程已经处于睡眠状态就必须经过规定的毫秒数b.如果线程正在等待输入或输出操作完成那么必须等待该操作完成c.如果线程调用了wait方法那么另外一个线程必须调用notifyAll或者notify方法d.如果线程正在等待另一个线程拥有的对象锁那么另一个线程必须放弃该锁的所有权5、下面这副图很好的反映了线程在不同情况下的状态变化。了解完多线程的相关知识下面来介绍一下在java中多线程的实现方式JAVA多线程实现方式JAVA多线程实现方式主要有以下三种1、继承Thread类2、实现Runnable接口3、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值只有最后一种是带返回值的。其中最常用的也是前两种实现方式。下面对前两种实现方式分别做下讲解。1、继承Thread类实现多线程继承Thread类的方法尽管被我列为一种多线程实现方式但Thread本质上也是实现了Runnable接口的一个实例它代表一个线程的实例并且启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法它将启动一个新线程并执行run()方法。这种方式实现多线程很简单通过自己的类直接extend Thread并复写run()方法就可以启动新线程并执行自己定义的run()方法。例如package thread;public class MyThread extends Thread {public void run() {System.out.println(run()方法正在执行);}}启动线程方式如下MyThread myThread1 new MyThread();MyThread myThread2 new MyThread();myThread1.start();myThread2.start();2、实现Runnable接口方式实现多线程如果自己的类已经extends另一个类就无法直接extends Thread此时必须实现一个Runnable接口。方法如下package thread;class OtherClass{public void print(String str){System.out.println(str);}}public class MyThread extends OtherClass implements Runnable {public void run() {System.out.println(run()正在执行);}}为了启动MyThread需要首先实例化一个Thread并传入自己的MyThread实例。具体方法如下MyThread myThread new MyThread();Thread thread new Thread(myThread);thread.start();事实上当传入一个Runnable target参数给Thread后Thread的run()方法就会调用target.run()参考JDK源代码public void run() {if (target ! null) {target.run();}}学会了线程的创建方式下面我们在举几个线程状态转换的例子3、线程状态的转换实例package thread;public class ThreadStateDemo extends Thread {Thread thread;public ThreadStateDemo() {thread new Thread(this);System.out.println(创建一个线程thread);thread.start();}public void run() {try {System.out.println(线程thread正在运行!);System.out.println(线程thread睡眠3秒中...!);Thread.sleep(3000); //静态方法使当前正在执行的线程睡眠3秒System.out.println(线程thread在睡眠后重新运行!);}catch(InterruptedException e) {System.out.println(线程被中断);}}public static void main(String[] args) {new ThreadStateDemo();System.out.println(主线程main结束!);}}【运行结果】如下创建一个线程thread主线程main结束!线程thread正在运行!线程thread睡眠3秒中...!线程thread在睡眠后重新运行!终止线程的实例package thread;public class ThreadShutDownDemo {public static void main(String args[]) {Runner runner new Runner();Thread thread new Thread(runner);thread.start();for(int i0;i10;i) {if(i%10!0) {System.out.println(在主线程中 i i);}}System.out.println(主线程main结束);//通知线程结束runner.shutDown();}}class Runner implements Runnable {//控制线程是否结束private boolean flag true;public void run() {int i0;while(flag true) {System.out.println(在子线程中 i i);}System.out.println(子线程结束);}//设置线程结束标志public void shutDown() {flag false;}}【运行结果】如下:在主线程中 i1在子线程中 i0在主线程中 i2在子线程中 i1在主线程中 i3在子线程中 i2在主线程中 i4在子线程中 i3在主线程中 i5在子线程中 i4在主线程中 i6在主线程中 i7在主线程中 i8在主线程中 i9主线程main结束在子线程中 i5子线程结束join()方法实例package thread;public class TheadJoinDemo {public static void main(String[] args) {Runner2 r new Runner2();Thread t new Thread(r);t.start();try {t.join();//主线程main将中断直到线程t执行完毕}catch(InterruptedException e) {}for(int i0;i5;i) {System.out.println(主线程 i);}}}class Runner2 implements Runnable {public void run() {for(int i0;i10;i) {System.out.println(子线程: i);}}}【运行结果】如下:子线程:0子线程:1子线程:2子线程:3子线程:4子线程:5子线程:6子线程:7子线程:8子线程:9主线程0主线程1主线程2主线程3主线程4介绍完以上几个实例我们下面对sleep()、wait()、yeid()、join()几个方法进行下区别总结sleep方法与wait方法的区别sleep方法是静态方法wait方法是非静态方法。sleep方法在时间到后会自己“醒来”但wait不能必须由其它线程通过notify(All)方法让它“醒来”。sleep方法通常用在不需要等待资源情况下的阻塞像等待线程、数据库连接的情况一般用wait。sleep/wait与yeld方法的区别调用sleep或wait方法后线程即进入block状态而调用yeld方法后线程进入runnable状态。wait与join方法的区别wait方法体现了线程之间的互斥关系而join方法体现了线程之间的同步关系。wait方法必须由其它线程来解锁而join方法不需要只要被等待线程执行完毕当前线程自动变为就绪。join方法的一个用途就是让子线程在完成业务逻辑执行之前主线程一直等待直到所有子线程执行完毕。线程的同步问题在实际应用中我们通常会遇到多线程安全问题。多线程安全问题当多条语句在操作同一线程共享数据是一个线程对多条语句只执行了一部分还没有执行完 此时另一个线程参与进来执行导致共享数据的错误。解决办法对多条操作共享数据的语句只能让一个线程都执行完在执行过程中其他线程不可以参与执行。Java 对于多线程的安全提供了专业的解决方式。线程的同步是保证多线程安全访问竞争资源的一种手段对于同步在具体的Java代码中需要完成一下两个操作把竞争访问的资源标识为private同步哪些修改变量的代码使用synchronized关键字同步方法或代码。synchronized(对象){代码块...}同步的前提1、必须要有两个或者两个以上的线程运行;2、必须是多个线程使用同一个锁;好处解决了多线程的安全问题;弊端多个线程需要判断锁较为消耗资源;注意非静态同步函数的对象锁为this静态同步函数所使用的锁是该方法所在类的字节码文件对象即类名.class,静态方法里的同步锁都是使用的是类的字节码对象。//静态同步函数锁public static synchronized void show(){ticket;System.out.println(Thread.currentThread().getName()runtime...ticket--);}下面来例举一个线程同步的例子(同步方法)package thread;public class SynchronizedThread {public static void main(String[] args) {User u new User(王某, 100);MyThread2 t1 new MyThread2(线程A, u, 10);MyThread2 t2 new MyThread2(线程B, u, -50);MyThread2 t3 new MyThread2(线程C, u, -60);MyThread2 t4 new MyThread2(线程D, u, -40);MyThread2 t5 new MyThread2(线程E, u, 20);MyThread2 t6 new MyThread2(线程F, u, 28);t1.start();t2.start();t3.start();t4.start();t5.start();t6.start();}}class MyThread2 extends Thread {private User u;private int y 0;MyThread2(String name, User u, int y) {super(name);this.u u;this.y y;}public void run() {u.oper(y);}}class User {private String code;private int cash;User(String code, int cash) {this.code code;this.cash cash;}public String getCode() {return code;}public void setCode(String code) {this.code code;}/*** 业务方法* param x 添加x万元*/public synchronized void oper(int x) {try {Thread.sleep(10L);this.cash x;System.out.println(Thread.currentThread().getName() 运行结束增加“ x ”当前用户账户余额为 cash);Thread.sleep(10L);} catch (InterruptedException e) {e.printStackTrace();}}Overridepublic String toString() {return User{ code code \ , cash cash };}}【运行结果】如下线程A运行结束增加“10”当前用户账户余额为110线程F运行结束增加“28”当前用户账户余额为138线程E运行结束增加“20”当前用户账户余额为158线程D运行结束增加“-40”当前用户账户余额为118线程C运行结束增加“-60”当前用户账户余额为58线程B运行结束增加“-50”当前用户账户余额为8下面是线程不同步的情况也就是去掉oper(int x)方法的synchronized修饰符然后再运行程序【运行结果】如下线程F运行结束增加“28”当前用户账户余额为128线程D运行结束增加“-40”当前用户账户余额为88线程B运行结束增加“-50”当前用户账户余额为38线程E运行结束增加“20”当前用户账户余额为58线程C运行结束增加“-60”当前用户账户余额为-2线程A运行结束增加“10”当前用户账户余额为8很显然上面的结果是错误的导致错误的原因是多个线程并发访问了竞争资源u并对u的属性做了改动。注意当去掉synchronized修饰符后线程不在同步每次运行的结果将都不一样可见同步的重要性。再把以上实例改为同步代码块方式对于同步除了同步方法外还可以使用同步代码块有时候同步代码块会带来比同步方法更好的效果。追其同步的根本的目的是控制竞争资源的正确的访问因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可因此Java引入了同步代码快的策略以提高性能。在上个例子的基础上对oper方法做了改动由同步方法改为同步代码块模式。代码如下package thread;/*** 同步代码块* author Chu**/public class SynchronizedThread2 {public static void main(String[] args) {User u new User(张三, 100);MyThread3 t1 new MyThread3(线程A, u, 10);MyThread3 t2 new MyThread3(线程B, u, -50);MyThread3 t3 new MyThread3(线程C, u, -60);MyThread3 t4 new MyThread3(线程D, u, -40);MyThread3 t5 new MyThread3(线程E, u, 20);MyThread3 t6 new MyThread3(线程F, u, 28);t1.start();t2.start();t3.start();t4.start();t5.start();t6.start();}}class MyThread3 extends Thread {private User u;private int y 0;MyThread3(String name, User u, int y) {super(name);this.u u;this.y y;}public void run() {u.oper(y);}}class User2 {private String code;private int cash;User2(String code, int cash) {this.code code;this.cash cash;}public String getCode() {return code;}public void setCode(String code) {this.code code;}/*** 业务方法* param x 添加x万元*/public void oper(int x) {try {Thread.sleep(10L);synchronized (this) {this.cash x;System.out.println(Thread.currentThread().getName() 运行结束增加“ x ”当前用户账户余额为 cash);}Thread.sleep(10L);} catch (InterruptedException e) {e.printStackTrace();}}Overridepublic String toString() {return User{ code code \ , cash cash };}}【运行结果】如下线程A运行结束增加“10”当前用户账户余额为110线程F运行结束增加“28”当前用户账户余额为138线程D运行结束增加“-40”当前用户账户余额为98线程E运行结束增加“20”当前用户账户余额为118线程C运行结束增加“-60”当前用户账户余额为58线程B运行结束增加“-50”当前用户账户余额为8用到线程的同步随之可能会带来死锁问题。导致死锁的原因两个线程互相等待竞争资源导致两边都无法得到资源而使自己无法运行。下面例举一个导致死锁的一个实例代码如下package thread;class Demo1{static Object obj1new Object();static Object obj2new Object();}class Demo2 implements Runnable{boolean flag;Demo2(boolean flag){this.flagflag;}Overridepublic void run(){if(flag){while(true){synchronized(Demo1.obj1){System.out.println(1);synchronized(Demo1.obj2){System.out.println(2);}}}}else{while(true){synchronized(Demo1.obj2){System.out.println(2);synchronized(Demo1.obj1){System.out.println(1);}}}}}}最后我再说说生产者消费者的问题对于多线程程序来说不管任何编程语言生产者和消费者模型都是最经典的。实际上准确说应该是“生产者-消费者-仓储”模型离开了仓储生产者消费者模型就显得没有说服力了。对于此模型应该明确一下几点1、生产者仅仅在仓储未满时候生产仓满则停止生产2、消费者仅仅在仓储有产品时候才能消费仓空则等待3、当消费者发现仓储没产品可消费时候会通知生产者生产4、生产者在生产出可消费产品时候应该通知等待的消费者去消费。此模型将要结合java.lang.Object的wait与notify、notifyAll方法来实现以上的需求。这是非常重要的。具体实现代码如下package thread;/*** Java线程生产者消费者模型* author Chu 2013-06-15 05:32:29*/public class ProductTest {public static void main(String[] args) {Godown godown new Godown(20);Consumer c1 new Consumer(80, godown);Consumer c2 new Consumer(30, godown);Consumer c3 new Consumer(20, godown);Producer p1 new Producer(5, godown);Producer p2 new Producer(5, godown);Producer p3 new Producer(5, godown);Producer p4 new Producer(10, godown);Producer p5 new Producer(20, godown);Producer p6 new Producer(35, godown);Producer p7 new Producer(50, godown);c1.start();c2.start();c3.start();p1.start();p2.start();p3.start();p4.start();p5.start();p6.start();p7.start();}}/** 仓库 */class Godown {public static final int max_size 100; //最大库存量public int curnum; //当前库存量Godown() {}Godown(int curnum) {this.curnum curnum;}/*** 生产指定数量的产品* param neednum*/public synchronized void produce(int neednum) {//测试是否需要生产while (neednum curnum max_size) {System.out.println(要生产的产品数量 neednum 超过剩余库存量 (max_size - curnum) 暂时不能执行生产任务!);try {//当前的生产线程等待wait();} catch (InterruptedException e) {e.printStackTrace();}}//满足生产条件则进行生产这里简单的更改当前库存量curnum neednum;System.out.println(已经生产了 neednum 个产品现仓储量为 curnum);//唤醒在此对象监视器上等待的所有线程notifyAll();}/*** 消费指定数量的产品* param neednum*/public synchronized void consume(int neednum) {//测试是否可消费while (curnum neednum) {try {//当前的生产线程等待wait();} catch (InterruptedException e) {e.printStackTrace();}}//满足消费条件则进行消费这里简单的更改当前库存量curnum - neednum;System.out.println(已经消费了 neednum 个产品现仓储量为 curnum);//唤醒在此对象监视器上等待的所有线程notifyAll();}}/** 生产者 */class Producer extends Thread {//生产产品的数量private int neednum;//仓库private Godown godown;Producer(int neednum, Godown godown) {this.neednum neednum;this.godown godown;}public void run() {//生产指定数量的产品godown.produce(neednum);}}/** 消费者 */class Consumer extends Thread {//生产产品的数量private int neednum;//仓库private Godown godown;Consumer(int neednum, Godown godown) {this.neednum neednum;this.godown godown;}public void run() {//消费指定数量的产品godown.consume(neednum);}}【运行结果】如下已经消费了20个产品现仓储量为0已经生产了5个产品现仓储量为5已经生产了5个产品现仓储量为10已经生产了5个产品现仓储量为15已经生产了20个产品现仓储量为35已经生产了50个产品现仓储量为85已经消费了80个产品现仓储量为5已经生产了10个产品现仓储量为15已经生产了35个产品现仓储量为50已经消费了30个产品现仓储量为20说明对于本例要说明的是当发现不能满足生产或者消费条件的时候调用对象的wait方法wait方法的作用是释放当前线程的所获得的锁并调用对象的notifyAll() 方法通知(唤醒)该对象上其他等待线程使得其继续执行。这样整个生产者、消费者线程得以正确的协作执行。notifyAll() 方法起到的是一个通知作用不释放锁也不获取锁。只是告诉该对象上等待的线程可以竞争执行了。以上这个例子仅仅是生产者消费者模型中最简单的一种表示在这个例子中如果消费者消费的仓储量达不到满足而又没有生产者则程序会一直处于等待状态这当然是不对的。实际上可以将此例进行修改修改为根据消费驱动生产同时生产兼顾仓库如果仓不满就生产并对每次最大消费量做个限制这样就不存在此问题了当然这样的例子更复杂更难以说明这样一个简单模型。