【代码】
package thread; public class MultiThread1 extends Thread{ public void run(){ for(int i=0; i<7; i++){ System.out.println("name:"+this.getName()+" i:"+ i+" "); } } public static void main(String[] args) { MultiThread1 tA = new MultiThread1(); tA.setName("AA"); MultiThread1 tB = new MultiThread1(); tB.setName("BB"); tA.run(); tB.run(); } }
【运行结果】
name:AA i:0
name:AA i:1
name:AA i:2
name:AA i:3
name:AA i:4
name:AA i:5
name:AA i:6
name:BB i:0
name:BB i:1
name:BB i:2
name:BB i:3
name:BB i:4
name:BB i:5
name:BB i:6
顺序执行的,说明调用方法不对,应该用start(),而不是run();
【代码】
package thread; public class MultiThread1 extends Thread{ public void run(){ for(int i=0; i<7; i++){ System.out.println("name:"+this.getName()+" i:"+ i+" "); } } public static void main(String[] args) { MultiThread1 tA = new MultiThread1(); tA.setName("AA"); MultiThread1 tB = new MultiThread1(); tB.setName("BB"); tA.start(); tB.start(); } }
【运行结果】
name:BB i:0
name:BB i:1
name:AA i:0
name:BB i:2
name:AA i:1
name:AA i:2
name:AA i:3
name:AA i:4
name:AA i:5
name:AA i:6
name:BB i:3
name:BB i:4
name:BB i:5
name:BB i:6
其实,start()方法内部调用了run()方法的。
当启动一个线程 的时候,是需要系统资源的支持,表现在代码中就是说 要进行初始化多线程的环境。然后再调用run()方法进行运行。
当单单调用run()方法的时候,系统并没有初始化多线程的环境,还是在一个线程中运行的。
start()的源代码:
public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0 || this != me) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } } private native void start0();start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void run() { // TODO Auto-generated method stub for(int i=0; i<10; i++){ System.out.println("name:"+this.getName()+" i:"+ i+" "); } } public static void main(String[] args) { MyRunnable mA = new MyRunnable(); mA.setName("AA"); MyRunnable mB = new MyRunnable(); mB.setName("BB"); Thread t1 = new Thread(mA); Thread t2 = new Thread(mB); t1.start(); t2.start(); } }
【运行结果】
name:AA i:0
name:AA i:1
name:AA i:2
name:BB i:0
name:AA i:3
name:BB i:1
name:AA i:4
name:BB i:2
name:AA i:5
name:BB i:3
name:AA i:6
name:BB i:4
name:AA i:7
name:BB i:5
name:BB i:6
name:BB i:7
name:BB i:8
name:BB i:9
name:AA i:8
name:AA i:9
Thread也是实现的Runnable接口
class Thread implements Runnable { //… public void run() { if (target != null) { target.run(); } } }
Thread和Runnable的区别:
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
【代码】
package thread; /** * 继承Thread类,不能资源共享 * */ class Thread2 extends Thread { private int count = 5; public void run() { for (int i = 0; i < 7; i++) { if (count > 0) { System.out.println("count= " + count--); } } } public static void main(String[] args) { Thread2 h1 = new Thread2(); Thread2 h2 = new Thread2(); Thread2 h3 = new Thread2(); h1.start(); h2.start(); h3.start(); } }
【运行结果】
count= 5
count= 5
count= 5
count= 4
count= 4
count= 3
count= 4
count= 2
count= 3
count= 1
count= 3
count= 2
count= 2
count= 1
count= 1
【代码】
package thread; class Thread3 implements Runnable{ private int ticket = 5; //5张票 public void run() { for (int i=0; i<=20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--); } } } public static void main(String[] args) { Thread3 my = new Thread3(); new Thread(my, "1号窗口").start(); new Thread(my, "2号窗口").start(); new Thread(my, "3号窗口").start(); } }
【运行结果】
1号窗口正在卖票5
3号窗口正在卖票3
2号窗口正在卖票4
3号窗口正在卖票1
1号窗口正在卖票2
总结一下吧:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。
isAlive判断线程是否启动
【代码】
MultiThread1 tA = new MultiThread1(); tA.setName("AA"); System.out.println("启动前 isAlive :"+ tA.isAlive()); tA.start(); System.out.println("启动后 isAlive :"+ tA.isAlive());
【运行结果】
启动前 isAlive :false
启动后 isAlive :true
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void run() { // TODO Auto-generated method stub for(int i=0; i<6; i++){ System.out.println("name:"+this.getName()+" i:"+ i+" "); } } public static void main(String[] args) throws InterruptedException { MyRunnable mA = new MyRunnable(); mA.setName("AA"); Thread t1 = new Thread(mA); t1.start(); for(int i = 1; i<50; i++){ System.out.println("main 线程: "+i); if(i == 3){ t1.join(); } } } }
【运行结果】
main 线程: 1
main 线程: 2
main 线程: 3
name:AA i:0
name:AA i:1
name:AA i:2
name:AA i:3
name:AA i:4
name:AA i:5
main 线程: 4
main 线程: 5
main 线程: 6
main 线程: 7
。。。
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void run() { // TODO Auto-generated method stub for(int i=0; i<3; i++){ System.out.println("name:"+this.getName()+" i:"+ i+" "); if(i == 1){ try { Thread.currentThread().sleep(2000); System.out.println("睡眠2秒--------"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { MyRunnable mA = new MyRunnable(); mA.setName("AA"); Thread t1 = new Thread(mA); t1.start(); } }
【运行结果】
name:AA i:0
name:AA i:1
睡眠2秒--------
name:AA i:2
睡眠2秒打印之前等待了2秒
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void run() { // TODO Auto-generated method stub System.out.println("线程开始"); try { Thread.currentThread().sleep(10000); System.out.println("线程完成休眠"); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("线程休眠被打断!"); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { MyRunnable mA = new MyRunnable(); mA.setName("AA"); Thread t1 = new Thread(mA); t1.start(); Thread.currentThread().sleep(2000); t1.interrupt();//2秒后终止线程 } }
【运行结果】
线程开始
线程休眠被打断!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at thread.MyRunnable.run(MyRunnable.java:17)
at java.lang.Thread.run(Thread.java:745)
在java程序中,只要前台有一个线程在运行,整个java程序进程不会消失,所以此时可以设置一个后台线程,这样即使java进程小时了,此后台线程依然能够继续运行。
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void run() { while(true){ System.out.println(Thread.currentThread().getName() + "在运行"); } } public static void main(String[] args) throws InterruptedException { MyRunnable mA = new MyRunnable(); mA.setName("AA"); Thread t1 = new Thread(mA); t1.setName("AA"); t1.start(); } }
【运行结果】
AA在运行
AA在运行
AA在运行
AA在运行
AA在运行
。。。
死循环,一直打印
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } MyRunnable(String name){ this.name = name; } @Override public void run() { for(int i=0;i<5;++i){ System.out.println(Thread.currentThread().getName()+"运行"+i); } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new MyRunnable("Thread1")); t1.setName("AA"); Thread t2 = new Thread(new MyRunnable("Thread2")); t2.setName("BB"); Thread t3 = new Thread(new MyRunnable("Thread3")); t3.setName("CC"); t2.setPriority(1); t2.setPriority(10); t3.setPriority(5); t1.start(); t2.start(); t3.start(); } }
【运行结果】
BB运行0
BB运行1
BB运行2
BB运行3
BB运行4
AA运行0
AA运行1
AA运行2
CC运行0
CC运行1
CC运行2
CC运行3
CC运行4
AA运行3
AA运行4
不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、
另外,主线程的优先级是5.
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } MyRunnable(String name){ this.name = name; } @Override public void run() { for(int i=0;i<5;++i){ System.out.println(Thread.currentThread().getName()+"运行"+i); if(i == 2 && Thread.currentThread().getName().equals("BB")){ Thread.currentThread().yield(); System.out.println("线程礼让"); } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new MyRunnable("Thread1")); t1.setName("AA"); Thread t2 = new Thread(new MyRunnable("Thread2")); t2.setName("BB"); t2.setPriority(6); t1.setPriority(6); t1.start(); t2.start(); } }
【运行结果】
BB运行0
BB运行1
BB运行2
AA运行0
线程礼让
BB运行3
AA运行1
AA运行2
AA运行3
AA运行4
BB运行4
同步和死锁
对于卖票系统,会有如下问题
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } MyRunnable(String name){ this.name = name; } private int count = 5; @Override public void run() { for(int i=1;i<10;i++){ if(count > 0){ try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("剩余票数: "+ count--); } } } public static void main(String[] args) throws InterruptedException { MyRunnable m = new MyRunnable("Thread1"); System.out.println(m.count); Thread t1 = new Thread(m); t1.setName("AA"); Thread t2 = new Thread(m); t2.setName("BB"); Thread t3 = new Thread(m); t3.setName("CC"); t1.start(); t2.start(); t3.start(); } }
【运行结果】
5
剩余票数: 5
剩余票数: 5
剩余票数: 5
剩余票数: 4
剩余票数: 3
剩余票数: 2
剩余票数: 1
剩余票数: 0
剩余票数: -1
这里出现了-1,显然这个是错的。,应该票数不能为负值。
如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,
其他的线程必须等到这个线程结束之后才能继续执行。
【使用线程同步解决问题】
采用同步的话,可以使用同步代码块和同步方法两种来完成。
语法格式:
synchronized(同步对象){
//需要同步的代码
}
但是一般都把当前对象this作为同步对象。
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } MyRunnable(String name){ this.name = name; } private int count = 5; @Override public void run() { for(int i=1;i<10;i++){ synchronized (this) { if(count > 0){ try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("剩余票数: "+ count--); } } } } public static void main(String[] args) throws InterruptedException { MyRunnable m = new MyRunnable("Thread1"); System.out.println(m.count); Thread t1 = new Thread(m); t1.setName("AA"); Thread t2 = new Thread(m); t2.setName("BB"); Thread t3 = new Thread(m); t3.setName("CC"); t1.start(); t2.start(); t3.start(); } }
【运行结果】
5
剩余票数: 5
剩余票数: 4
剩余票数: 3
剩余票数: 2
剩余票数: 1
语法格式为synchronized 方法返回类型方法名(参数列表){
// 其他代码
}
【代码】
package thread; public class MyRunnable implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } MyRunnable(String name){ this.name = name; } private int count = 5; @Override public void run() { for(int i=1;i<10;i++){ sale(); } } public synchronized void sale(){ if(count > 0){ try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("剩余票数: "+ count--); } } public static void main(String[] args) throws InterruptedException { MyRunnable m = new MyRunnable("Thread1"); System.out.println(m.count); Thread t1 = new Thread(m); t1.setName("AA"); Thread t2 = new Thread(m); t2.setName("BB"); Thread t3 = new Thread(m); t3.setName("CC"); t1.start(); t2.start(); t3.start(); } }
【运行结果】
5
剩余票数: 5
剩余票数: 4
剩余票数: 3
剩余票数: 2
剩余票数: 1
此处列举经典的生产者和消费者问题。
【代码】
信息类
package lock; public class Info { private String name = "wangting"; private int age = 20; 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; } }
生产者
package lock; 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("wangting"); try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.info.setAge(20); flag=false; }else{ this.info.setName("齐天大圣"); try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.info.setAge(10000); flag=true; } } } }
消费者
package lock; 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()); } } }
测试代码
package lock; public class Test { 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(); } }
【运行结果】
wangting<---->10000
wangting<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
wangting<---->10000
齐天大圣<---->20
齐天大圣<---->10000
大家可以从结果中看到,名字和年龄并没有对应上。
齐天大圣<---->20
齐天大圣<---->10000
1)加入同步
2)加入等待和唤醒
【代码】
信息类
package lock; public class Info { private String name = "wangting"; private int age = 20; 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()); } }
生产者
package lock; 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){ try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.info.set("wangting",20); flag=false; }else{ try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } this.info.set("齐天大圣",10000); flag=true; } } } }
消费者
package lock; 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(); } } }
测试代码
package lock; public class Test { 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(); } }
【运行结果】
齐天大圣<===>10000
齐天大圣<===>10000
wangting<===>20
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
齐天大圣<===>10000
wangting<===>20
以上, 对应错乱的问题解决了。
但是还是出现了重复读取的问题,也肯定有重复覆盖的问题。如果想解决这个问题,就需要使用Object类帮忙
,我们可以使用其中的等待和唤醒操作。
要完成上面的功能,我们只需要修改Info类,在其中加上标志位,并且通过判断标志位完成等待和唤醒的操作,代码如下:
【代码】
package lock; public class Info { private String name = "wangting"; private int age = 20; private boolean flag = false;// 标志位 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 boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public synchronized void set(String name, int age){ if(!flag){ try { super.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block 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 (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try{ Thread.sleep(100); }catch (Exception e) { e.printStackTrace(); } System.out.println(this.getName()+"<===>"+this.getAge()); flag=true; super.notify(); } }
【运行结果】
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20
齐天大圣<===>10000
wangting<===>20