------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、多线程
1.进程
1).线程(例:FlashGet)2).多线程存在的意义3).线程的创建方式4).多线程的特性
进程:是一个正在执行的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立控制单元。线程控制着进程的执行。
一个进程至少有一个线程。JavaVM启动的时候会有一个进程Java.exe
该进程中至少一个线程负责Java程序的执行,而且这个线程运行的代码存在于main方法中。该线程称为主线程。
2,如何在自定义的代码中自定义一个线程呢?
通过对api的查找,Java已经提供了对线程这类事物的描述,就是Thread类。
a.创建线程的第一种方式:继承Thread类
步骤:
1),定义类继承Thread2),复写Thread类中的run方法
目的:将自定义代码存储在run方法中,让线程运行。
3),调用线程的start方法
该方法两个作用:启动线程,调用run方法。
b.创建线程的第二种方式:实现Runnable接口
步骤:
1),定义类实现Runnable接口2),覆盖Runnable接口中的run方法将线程要运行的代码存放在该run方法中3),通过Thread类建立对象4),将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
为什么将Runnable接口的子类对象传递给Thread的构造函数?
因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的代码,就必须明确该run方法所属的对象。
5),调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现与继承方式的区别:
继承Thread:线程代码存放在Thread子类的run方法中。实现Runnable:线程代码存放在接口的子类的run方法中。实现方式的好处:避免单继承的局限性。在定义线程时,建议使用实现接口方式。
3.多线程的运行出现了安全性的问题/* 发现运行结果每一次都不同。因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行。 在某一个时刻,只能有一个线程在运行(多核处理器除外)。 cpu在做着快速的切换,以达到看上去同时运行的效果。 */ class Demo extends Thread { public void run() { for(int x = 0;x<60;x++) System.out.println("Demo run"); } } class ThreadDemo { public static void main(String[] args) { Demo d = new Demo(); d.start();//开启线程并执行该线程的run方法 // d.run()//仅仅是对象调用方法,线程创建了,但是没有运行。 for(int x = 0;x<60;x++) System.out.println("main run"); } }
问题的原因:
当多条语句在操作多个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条语句在操作共享数据的语句,只能让一个线程执行完,再执行过程中,其他线程不可以参与运行。
Java对于多线程的安全问题提供了专业的解决方案:同步代码块。
synchronized(对象)
{
需要被同步代码;
}
对象如同锁,持有所得线程可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
必须保证同步中只有一个线程在运行。
4.同步的前提:
1),必须要有两个或者两个以上的线程。2),必须必须是多个线程使用同一个锁。
好处:解决了多线程的安全问题弊端:多个线程都要半段锁,较为消耗资源。
同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步函数使用的锁是this。
如果同步函数被静态修饰后,那么锁是什么呢?
通过验证,发现不是this,因为静态方法中不能定义她this。静态进内存时,内存中没有本类的对象,但是一定有该类对应的字节码对象。
类名.class 该对象的类型是class。静态同步方法使用的锁是该方法所在类的字节码文件对象。类名.class。
5.死锁:同步中嵌套同步
死锁示例:6.线程间通信:其实就是多个线程在操作同一个资源,但是操作的动作不同。class SiSuo implements Runnable { private boolean flag; SiSuo(boolean flag) { this.flag = flag; } //复写run方法 public void run() { if(flag) { //同步中嵌套同步 synchronized(MyLock.lock1) { System.out.println("if lock1"); //此处产生死锁 synchronized(MyLock.lock2) { System.out.println("if lock2"); } } } else { //同步中嵌套同步 synchronized(MyLock.lock2) { System.out.println("else lock2"); //此处产生死锁 synchronized(MyLock.lock1) { System.out.println("else lock1"); } } } } } class MyLock { //Object建立对象 static Object lock1 = new Object(); static Object lock2 = new Object(); } class SiSuoDemo { public static void main(String[] args) { //建立线程 Thread Thread1 = new Thread(new SiSuo(true)); Thread Thread2 = new Thread(new SiSuo(false)); //启动线程 Thread1.start(); Thread2.start(); } }
思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
1),这些方法存在于同步中。2),使用这些方法时必须要标识所属的同步锁。3),锁可以是任意对象,所以任意对象调用的方法一定定义在Object类中。
思考2:wait(),sleep()有什么区别?
wait()释放资源,释放锁。sleep()释放资源,不释放锁。
线程间通信代码示例:
以上代码可以经过优化,优化结果如下:class Res { String name; String sex; boolean flag = false; } class Input implements Runnable { private Res r; Input(Res r) { this.r = r; } //复写run方法 public void run() { int x =0; while(true) { //同步代码块开始 synchronized(r) { //判断标记 if(r.flag) try{r.wait();}catch(Exception e){}//使线程冻结 if(x==0) { r.name="DuckOne"; r.sex="SuperMan"; } else { r.name="莉莉"; r.sex="女女"; } x = (x+1)%2; r.flag = true;//修改标记 r.notify();//唤醒线程 } } } } class Output implements Runnable { private Res r; Output(Res r) { this.r = r; } public void run() { while(true) { //同步代码块开始 synchronized(r) { //判断标记 if(!r.flag) try{r.wait();}catch(Exception e){}//使线程冻结 System.out.println(r.name+"....."+r.sex); r.flag = false;//修改标记 r.notify();//唤醒线程 } } } } class InputOutput { public static void main(String[] args) { Res r = new Res(); Input in = new Input(r); Output out = new Output(r); //创建线程 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //启动线程 t1.start(); t2.start(); } }
JDK 1.5中提供了多线程升级解决方案。将同步Synchronized替换出显示的lock操作。将Object中的wait,notifyAll,替换成了Condition对象。该对象可以lock锁进行获取。class Res { private String name; private String sex; private boolean flag = false; public synchronized void set(String name,String sex) { //判断标记 if(flag) { try { //冻结线程 this.wait(); } catch(InterruptedException e) { e.printStackTrace(); } } this.name = name; this.sex = sex; flag = true; this.notify();//唤醒进程 } public synchronized void out() { //判断标记 if(!flag) { try { this.wait(); } catch(InterruptedException e) { e.printStackTrace(); } } System.out.println(name+"....."+sex); flag = true; this.notify();//唤醒进程 } } class Input implements Runnable { private Res r; Input(Res r) { this.r = r; } //复写run方法 public void run() { int x =0; while(true) { if(x==0) r.set("DuckOne","SuperMan"); else r.set("莉莉","女女"); x = (x+1)%2; } } } class Output implements Runnable { private Res r; Output(Res r) { this.r = r; } //复写run方法 public void run() { while(true) { r.out(); } } } class InputOutput { public static void main(String[] args) { Res r = new Res(); //创建线程与启动线程 new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); } }
7.停止线程
1),定义循环结束标记
因为线程运行代码一般都是循环,只要控制了循环即可。
2),使用interrupt(中断)方法
该方法是结束线程的冻结状态,使线程回到运行中的状态来。
注:stop方法已经过时不再使用。
以上代码可以采用JDK1.5以上的新机制修改,修改后较为理想。/* 需求:生产者生产商品,供消费者使用 有两个或者多个生产者,生产一次就等待消费一次 有两个或者多个消费者,等待生产者生产一次就消费掉 */ class Resourse { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name) { //用while循环判断标记 while(flag) { try { this.wait();//冻结线程 } catch(InterruptedException e) { e.printStackTrace(); } } this.name = name +"---"+count++; System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name); flag = true; this.notifyAll();//唤醒所有线程 } public synchronized void out() { //用while循环判断标记 while(!flag) { try { this.wait();//冻结线程 } catch(InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"..消费者......."+this.name); flag = false; this.notifyAll();//唤醒所有线程 } } class Producer implements Runnable { private Resourse res; Producer(Resourse res) { this.res = res; } //复写run方法 public void run() { while(true) { res.set("商品"); } } } class Consumer implements Runnable { private Resourse res; Consumer(Resourse res) { this.res = res; } //复写run方法 public void run() { while(true) { res.out(); } } } class ProCon { public static void main(String[] args) { Resourse res = new Resourse(); Producer pro = new Producer(res); Consumer con = new Consumer(res); //创建线程 Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(pro); Thread t4 = new Thread(con); //启动线程 t1.start(); t2.start(); t3.start(); t4.start(); } }
/* 需求:生产者生产商品,供消费者使用 有两个或者多个生产者,生产一次就等待消费一次 有两个或者多个消费者,等待生产者生产一次就消费掉 */ import java.util.concurrent.locks.*; class Resourse { private String name; private int count = 1; private boolean flag = false; private Lock lock = new ReentrantLock(); //创建两Condition对象,分别来控制等待或唤醒本方和对方线程 private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); public void set(String name)throws InterruptedException { //采用新机制获取锁 lock.lock(); try { //用while循环判断标记 while(flag) condition_pro.await();//生产者线程冻结 this.name = name +"---"+count++; System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name); flag = true; condition_con.signal();//唤醒消费者线程 } finally { //释放锁 lock.unlock(); } } public void out()throws InterruptedException { //采用新机制获取锁 lock.lock(); try { //用while循环判断标记 while(!flag) condition_con.await();//消费者线程冻结 System.out.println(Thread.currentThread().getName()+"..消费者......."+this.name); flag = false; condition_pro.signal();//唤醒生产者线程 } finally { //释放锁 lock.unlock(); } } } //定义生产者 class Producer implements Runnable { private Resourse res; Producer(Resourse res) { this.res = res; } //复写run方法 public void run() { while(true) { try { res.set("商品"); } catch(InterruptedException e) { e.printStackTrace(); } } } } //定义消费者 class Consumer implements Runnable { private Resourse res; Consumer(Resourse res) { this.res = res; } //复写run方法 public void run() { while(true) { try { res.out(); } catch(InterruptedException e) { e.printStackTrace(); } } } } class ProCon { public static void main(String[] args) { Resourse res = new Resourse(); Producer pro = new Producer(res); Consumer con = new Consumer(res); //创建线程 Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(pro); Thread t4 = new Thread(con); //启动线程 t1.start(); t2.start(); t3.start(); t4.start(); } }