首页 > 代码库 > 多线程常用基础
多线程常用基础
一:进程与线程的描述:
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1~n个线程。(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
二:实现多线程的常用方式:
1.继承Thread类
2.实现Runnable接口
三:对于直接继承Thread
看例子:
public class Zy extends Thread{ private String name; public Zy(){} public Zy(String name){ this.name = name; } public void run(){ for (int i=0;i<5;i++){ System.out.println(name+"执行 "+i+"次"); } }
public class Test { public static void main(String[] args) { Zy z1 = new Zy("z1"); Zy z2 = new Zy("z2"); z1.run(); //这里不能直接调用test()方法 z2.run(); //这里的run方法手是重写Thread的run方法,然后调用才能start() } }
实现效果:
z1执行 0次
z1执行 1次
z1执行 2次
z1执行 3次
z1执行 4次
z2执行 0次
z2执行 1次
z2执行 2次
z2执行 3次
z2执行 4次
这种方法执行顺序是不对的,应该调用start()方法
public static void main(String[] args) { Zy z1 = new Zy("z1"); Zy z2 = new Zy("z2"); z1.start(); z2.start(); }
注:只有重写了Thread里的run()方法start()方法才能实现效果。
实现效果
z2执行 0次
z1执行 0次
z2执行 1次
z1执行 1次
z2执行 2次
z1执行 2次
z2执行 3次
z1执行 3次
z2执行 4次
z1执行 4次
因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的。
至于为什么不能直接调用run()方法,应该是线程的运行需要本地操作系统的支持。
此处调用的是start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。
通过实现Runnable接口
public class Zy implements Runnable{ private String name; public Zy(){} public Zy(String name){ this.name = name; } public void run(){ for (int i=0;i<5;i++){ System.out.println(name+"执行 "+i+"次"); } } }
public static void main(String[] args) { Zy z1 = new Zy("z1"); Thread t1 = new Thread(z1); Zy z2 = new Zy("z2"); Thread t2 = new Thread(z2); t1.start(); t2.start(); }
//执行结果
第一次:
z1执行 0次
z1执行 1次
z2执行 0次
z2执行 1次
z2执行 2次
z2执行 3次
z2执行 4次
z1执行 2次
z1执行 3次
z1执行 4次
第二次:
z1执行 0次
z1执行 1次
z1执行 2次
z1执行 3次
z1执行 4次
z2执行 0次
z2执行 1次
z2执行 2次
z2执行 3次
z2执行 4次
//每次执行结果大致都不一样,如果大致一样可能你的操作系统比较稳定,呵呵
关于选择继承Thread还是实现Runnable接口?
其实查看源码发现
public class Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does. */ private static native void registerNatives(); static { registerNatives(); }
Thread也是实现了Runnable接口,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。关于代理模式 我不太清楚 不过这:http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144847.html
Thread和Runnable的区别:
1.如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
这里实现继承Thread
public class Zy extends Thread{
private int count=5;
public void run(){
for (int i = 0; i < 6; i++) {
if (count > 0) {
System.out.println("数量= " + count--);
}
}
}
public static void main(String[] args) {
Zy z1 = new Zy();
Thread t1 = new Thread(z1);
Zy z2 = new Zy();
Thread t2 = new Thread(z2);
t1.start();
t2.start();
}
}
执行效果
数量= 5 数量= 4 数量= 3 数量= 2 数量= 1 数量= 5 数量= 4 数量= 3 数量= 2 数量= 1
这种说明资源并不能共享,换成runnable接口
public class Zy implements Runnable{ private int count=5; public void run(){ for (int i = 0; i < 6; i++) { if (count > 0) { System.out.println("数量= " + count--); } } } public static void main(String[] args) { Zy zy = new Zy(); new Thread(zy,"1号窗口").start(); new Thread(zy, "2号窗口").start(); new Thread(zy, "3号窗口").start(); } }
(可能实现的)实现效果
数量= 5 数量= 2 数量= 1 数量= 3 数量= 4
总结一下吧:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
另外:main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM事实上就是在操作系统中启动了一个进程。
四:判断线程是否启动
public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { Zy zy = new Zy(); Thread demo = new Thread(zy); System.out.println("线程启动之前---》" + demo.isAlive()); demo.start(); System.out.println("线程启动之后---》" + demo.isAlive()); }
执行结果
线程启动之前---》false 线程启动之后---》true Thread-0 Thread-0 Thread-0
主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。
五:线程的强制执行join:
public class Zy implements Runnable{ public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { Zy zy = new Zy(); Thread demo = new Thread(zy,"线程"); demo.start(); for(int i=0;i<10;++i){ if(i>5){ try{ demo.join(); //强制执行demo }catch (Exception e) { e.printStackTrace(); } } System.out.println("main 线程执行-->"+i); } } }
执行结果
main 线程执行-->0 main 线程执行-->1 main 线程执行-->2 main 线程执行-->3 main 线程执行-->4 main 线程执行-->5 线程 线程 //就是在主线程没有结束之前强行执行 线程 main 线程执行-->6 main 线程执行-->7 main 线程执行-->8 main 线程执行-->9
六:线程休眠sleep:
public class Zy implements Runnable{ public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(2000); //2000指2秒 } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + i); } } public static void main(String[] args) { Zy zy = new Zy(); Thread demo = new Thread(zy, "线程"); demo.start(); } }
执行结果
线程0
线程1
线程2 //没两秒打印一个
七,线程中断interrupt()
public class Zy implements Runnable{ public void run() { System.out.println("执行run方法"); try { Thread.sleep(10000); System.out.println("线程完成休眠"); } catch (Exception e) { System.out.println("休眠被打断"); return; //返回到程序的调用处 } System.out.println("线程正常终止"); } public static void main(String[] args) { Zy zy = new Zy(); Thread demo = new Thread(zy, "线程"); demo.start(); try{ Thread.sleep(2000); }catch (Exception e) { e.printStackTrace(); } demo.interrupt(); //2s后中断线程 }
}
执行结果
执行run方法 -----间隔两秒后 休眠被打断
在java程序中,只要前台有一个线程在运行,整个java程序进程不会消失,所以此时可以设置一个后台线程,这样即使java进程消失了,此后台线程依然能够继续运行。主线程中断 不会打断子线程
public class Zy implements Runnable{ public void run() { while (true) { System.out.println(Thread.currentThread().getName() + "在运行"); } } public static void main(String[] args) { Zy zy = new Zy(); Thread demo = new Thread(zy, "线程"); demo.setDaemon(true); demo.start(); } }
八:线程的优先级
查看Thread源码发现
/** * The minimum priority that a thread can have. //最低优先级 */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10;
//MIN_PRIORITY值越小优先级越低
public class Zy implements Runnable{ public void run() { for(int i=0;i<5;++i){ System.out.println(Thread.currentThread().getName()+"运行"+i); } } public static void main(String[] args) { Thread h1=new Thread(new Zy(),"A"); Thread h2=new Thread(new Zy(),"B"); Thread h3=new Thread(new Zy(),"C"); h1.setPriority(8); h2.setPriority(2); h3.setPriority(6); h1.start(); h2.start(); h3.start(); } }
打印效果
A运行0
A运行1
A运行2
A运行3
A运行4
C运行0
C运行1
C运行2
C运行3
C运行4
B运行0
B运行1
B运行2
B运行3
B运行4
但是请读者不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、不知道是不是
NORM_PRIORITY值越高,谁就可以先拿到cpu资源,我执行了n次结果都是一样的?
另外,主线程的优先级是5.
九:线程的礼让。
在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。
public class Zy implements Runnable{ public void run() { for(int i=0;i<5;++i) { System.out.println(Thread.currentThread().getName() + "运行" + i); if (i == 3) { System.out.println("线程的礼让"); Thread.currentThread().yield(); } } } public static void main(String[] args) { Thread h1=new Thread(new Zy(),"A"); Thread h2=new Thread(new Zy(),"B"); h1.start(); h2.start(); } }
执行效果
A运行0
A运行1
A运行2
A运行3
线程的礼让
B运行0
B运行1
B运行2
B运行3
线程的礼让
A运行4
B运行4
十:同步和死锁。
public class Zy implements Runnable{ private int count=5; public void run() { for(int i=0;i<10;++i){ if(count>0){ try{ Thread.sleep(1000); //休息一秒 }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(count--); } } } public static void main(String[] args) { Zy zy = new Zy(); Thread h1=new Thread(zy); Thread h2=new Thread(zy); Thread h3=new Thread(zy); h1.start(); h2.start(); h3.start(); } }
执行效果
5 3 4 2 2 2 1 0 -1
这里应该是线程之间出现了纷争,
如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,
其他的线程必须等到这个线程结束之后才能继续执行。
【同步代码块】:
语法格式:
synchronized(同步对象){
//需要同步的代码
}
public class Zy implements Runnable{ private int count=5; public void run() { for(int i=0;i<10;++i){ synchronized (this) { if (count > 0) { try { Thread.sleep(1000); //休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count--); } } } } public static void main(String[] args) { Zy zy = new Zy(); Thread h1=new Thread(zy); Thread h2=new Thread(zy); Thread h3=new Thread(zy); h1.start(); h2.start(); h3.start(); } }
执行结果
5 4 3 2 1 //没秒输出一个
【同步方法】
也可以采用同步方法。
语法格式为synchronized 方法返回类型方法名(参数列表){
// 其他代码
}
public class Zy implements Runnable{ private int count=5; public void run() { for (int i = 0; i < 10; ++i) { sale(); } } public synchronized void sale() { if (count > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count--); } } public static void main(String[] args) { Zy zy = new Zy(); Thread h1=new Thread(zy); Thread h2=new Thread(zy); Thread h3=new Thread(zy); h1.start(); h2.start(); h3.start(); } }
//输出结果一样,不信 你可以试一下
死锁
当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。
【生产者和消费者问题】
/* *公共信息类 */ public class Info { public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } private String name = "Rollen"; private int age = 20; } /** *消费者类 */ public class Consumer implements Runnable { private Info info=null; public Consumer(Info info){ this.info=info; } public void run(){ for(int i=0;i<25;++i){ try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } System.out.println(this.info.getName()+"<---->"+this.info.getAge()); } } } /** *生产类 */ public class Producer implements Runnable { private Info info=null; Producer(Info info){ this.info=info; } public void run(){ boolean flag=false; for(int i=0;i<25;++i){ if(flag){ this.info.setName("Rollen"); try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.info.setAge(20); flag=false; }else{ this.info.setName("chunGe"); try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.info.setAge(100); flag=true; } } } }
测试类
public static void main(String[] args) { Info info = new Info(); Producer pro = new Producer(info); Consumer con = new Consumer(info); new Thread(pro).start(); new Thread(con).start(); }
测试结果
Rollen<---->100 chunGe<---->20 chunGe<---->100 Rollen<---->100 chunGe<---->20 chunGe<---->20 Rollen<---->100 Rollen<---->100 Rollen<---->100 chunGe<---->20 chunGe<---->100 chunGe<---->20 chunGe<---->20 chunGe<---->20 chunGe<---->100 Rollen<---->20 chunGe<---->100 Rollen<---->20 chunGe<---->100 chunGe<---->20 Rollen<---->100 chunGe<---->20 Rollen<---->100 Rollen<---->20 chunGe<---->20 //名字和年龄并不对应
那么如何解决呢?
1)加入同步
2)加入等待和唤醒
首先是加入同步
class Info { public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public synchronized void set(String name, int age){ this.name=name; try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.age=age; } public synchronized void get(){ try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } System.out.println(this.getName()+"<===>"+this.getAge()); } private String name = "Rollen"; private int age = 20; } /** * 生产者 * */ class Producer implements Runnable { private Info info = null; Producer(Info info) { this.info = info; } public void run() { boolean flag = false; for (int i = 0; i < 25; ++i) { if (flag) { this.info.set("Rollen", 20); flag = false; } else { this.info.set("ChunGe", 100); flag = true; } } } } /** * 消费者类 * */ class Consumer implements Runnable { private Info info = null; public Consumer(Info info) { this.info = info; } public void run() { for (int i = 0; i < 25; ++i) { try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } this.info.get(); } } } /** * 测试类 * */ class hello { public static void main(String[] args) { Info info = new Info(); Producer pro = new Producer(info); Consumer con = new Consumer(info); new Thread(pro).start(); new Thread(con).start(); } }
实现效果
Rollen<===>20 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 ChunGe<===>100 ChunGe<===>100 ChunGe<===>100 ChunGe<===>100 ChunGe<===>100 ChunGe<===>100 ChunGe<===>100 ChunGe<===>100
名字和年龄是对应了,但是存在有重复覆盖的问题
如果想解决这个问题,就需要使用Object类帮忙了、
,我们可以使用其中的等待和唤醒操作。
要完成上面的功能,我们只需要修改Info类饥渴,在其中加上标志位,并且通过判断标志位完成等待和唤醒的操作,代码如下
public synchronized void set(String name, int age){ if(!flag){ try{ super.wait(); }catch (Exception e) { e.printStackTrace(); } } this.name=name; try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.age=age; flag=false; super.notify(); } public synchronized void get(){ if(flag){ try{ super.wait(); }catch (Exception e) { e.printStackTrace(); } } try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } System.out.println(this.getName()+"<===>"+this.getAge()); flag=true; super.notify(); } private String name = "Rollen"; private int age = 20; private boolean flag=false;
结果
Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20 ChunGe<===>100 Rollen<===>20
问题解决
多线程常用基础