线程的基本了解
1、线程的实现方式
线程有两种实现方式,分别为继承thread、实现runnable接口。差异性:实现方式避免了类的继承单一性,且对于多个线程同时访问同一个资源时更便捷。
(1)继承thread
class testthread extends thread { @override public void run() { for (int i = 0; i <= 100; i++) { system.out.println(thread.currentthread().getname()+":"+i); } } } public class testthreadmethod { public static void main(string[] args) { testthread th1 = new testthread(); th1.setname("testthread"); th1.start(); } }
(2)实现runnable接口
class testrunnable implements runnable{ @override public void run() { for (int i=1; i <= 100; i++) { system.out.println(thread.currentthread().getname() + ":" + i); } } } public class testmain { public static void main(string[] args) { testrunnable tr = new testrunnable(); thread th1 = new thread(tr, "th1"); thread th2 = new thread(tr, "th2"); th1.start(); th2.start(); } }
(3)线程的生命周期
2、线程的常用方法
start():线程的开启。同一个线程在结束时只能开启一次,否则报错。
setname():设置当前线程的名称。
getname():获取当前线程的名称。
currentthread():获取当前线程的对象。
priority():线程的级别。静态值有,0/5(默认)/10。值越高,代表争夺cpu的使用权的几率越大,但不是绝对。
yield():线程让步。当前线程a调用yield()方法时,释放cpu的使用权,重新与其他线程b争夺cpu的使用权。
join():线程加入。在a线程中,调用b线程的join方法时,a线程的程序等待b线程的程序执行完才能继续执行a线程。
sleep():线程睡眠。当线程执行该方法时,进入设置的睡眠时间后继续执行程序。
isalive():线程是否在活动,返回值为boolean。
wait():线程等待。必须在同步方法中使用。
notify()/notifyall():线程唤醒,与wait()结合使用,用于线程通信。
注意:(1)线程在同步时,wait()会释放锁,而join()与sleep()方法不会释放锁。
(2)wait()、notify()、notifyall()严格来说不算线程中的方法,这三个方法是定义在object对象中。
class testthread extends thread { @override public void run() { for (int i = 0; i <= 100; i++) { system.out.println(thread.currentthread().getname()+":"+i); } } } public class testthreadmethod { public static void main(string[] args) { testthread th1 = new testthread(); th1.setname("testthread"); th1.setpriority(thread.max_priority); th1.start(); thread.currentthread().setname("=======mainthread"); for (int i = 0; i <= 100; i++) { system.out.println(thread.currentthread().getname() + ":" + i); if (i % 10 == 0) { thread.yield(); } if (i == 20) { try { th1.join(); system.out.println(th1.isalive()); th1.start(); } catch (interruptedexception e) { e.printstacktrace(); } } } } }
3、线程的同步
线程的同步分为两种:同步代码块和同步方法块。
class testthread extends thread { static int ticket = 100; static object obj = new object(); @override public void run() { /*1、同步代码块时,只能同步需要的代码块,其余无关代码块不行,会影响同步; * 2、同步监视器用this,代表当前对象,此时继承方式,对象是多个,所以无法同步; * 此两种情况出现重复输出情况,如下的this情况 */ /*synchronized (this) { while(true) { try { thread.currentthread().sleep(100); } catch (interruptedexception e) { // todao auto-generated catch block e.printstacktrace(); } if (ticket <= 0) { break; } system.out.println(thread.currentthread().getname() + "窗口:" +ticket--); } }*/ while(true) { synchronized (obj) {//obj充当锁时必须是静态的,否则多个对象代表的锁是各自的。 try { thread.currentthread().sleep(100); } catch (interruptedexception e) { e.printstacktrace(); } if (ticket <= 0) { break; } system.out.println(thread.currentthread().getname() + "窗口:" +ticket--); } } } } public class testmain { public static void main(string[] args) { testthread th1 = new testthread(); testthread th2 = new testthread(); th1.setname("th1"); th2.setname("th2"); th1.start(); th2.start(); } }
class testthread implements runnable { int ticket = 100; @override public void run() { while(true) { importnum(); } } /** * 同步方法块时,同步锁为当前对象.所以方法的类必须是同一个对象 */ private synchronized void importnum() { if (ticket <= 0) { return; } system.out.println(thread.currentthread().getname() + "窗口:" + ticket--); } } public class testmain { public static void main(string[] args) { testthread th = new testthread(); thread th1 = new thread(th, "th1"); thread th2 = new thread(th, "th2"); th1.start(); th2.start(); } }
注:同步在thread与runnable两种方式的实现中有很大的区别,详见上例。
拓展示例:
class singleton { private singleton() { } public static singleton instance = null; public static singleton getsingleton() { /** * 原程序直接判断,创建实例,返回。在多线程中,存在线程安全问题,会创建多个singleton; * 开发中为避免这情况发生,采用下面同步的实现方式。 */ /** if (instance == null) { instance = new singleton(); } return instance; */ if (instance == null) {//提高同步效率,后面访问方法的对象,无需再次等待 synchronized(singleton.class) {//采用类本身的对象来充当锁 if (instance == null) { instance = new singleton(); } } } return instance; } } public class testmain { public static void main(string[] args) { singleton st1 = singleton.getsingleton(); singleton st2 = singleton.getsingleton(); } }
4、线程的死锁
死锁:当两个线程同时需要对方手中的锁时,各自都在等对方放弃手中的锁,而使程序无限期的等待下去,称为死锁。
public class testmain { public static void main(string[] args) { stringbuffer sb1 = new stringbuffer(); stringbuffer sb2 = new stringbuffer(); new thread(){ public void run() { synchronized(sb1) { sb1.append("a"); try { thread.currentthread().sleep(10); } catch (interruptedexception e) { e.printstacktrace(); } synchronized(sb2) { sb2.append("b"); system.out.println("sb1:" + sb1.tostring()); system.out.println("sb2:" + sb2.tostring()); } } }; }.start(); new thread(){ public void run() { synchronized(sb2) { sb2.append("c"); try { thread.currentthread().sleep(10); } catch (interruptedexception e) { e.printstacktrace(); } synchronized(sb1) { sb1.append("d"); system.out.println("sb1:" + sb1.tostring()); system.out.println("sb2:" + sb2.tostring()); } } }; }.start(); } }
上述例子中,第一个线程执行第一个同步块代码后,进行睡眠,握住sb1的锁;此时第二个线程可能已经开启运行,获取sb2的锁,进行同步代码块方法,进行睡眠。之后第一个线程睡眠时间过后要进行第二个同步代码块的执行,这时sb2的锁握在了第二个线程,而第二个线程在醒来后要获取第二块同步方法的锁时,这时的锁在第一个线程中。两个线程同时在等待对方释放手中的锁,导致了死锁的出现。
5、线程的通信
/** * 生产者 --> 店员(数量最多存放20) --> 消费者 * */ class clerk { int num; public synchronized void addproduct() { if (num >= 20) { try { wait(); } catch (interruptedexception e) { // todo auto-generated catch block e.printstacktrace(); } } notifyall(); num++; system.out.println(thread.currentthread().getname() + ":" + num); } public synchronized void consume() { if (num == 0) { try { wait(); } catch (interruptedexception e) { // todo auto-generated catch block e.printstacktrace(); } } notifyall(); system.out.println(thread.currentthread().getname() + ":" + num); num--; } } class customer implements runnable { clerk clerk; public customer(clerk clerk) { this.clerk = clerk; } public void run() { while(true) { try { thread.currentthread().sleep(10); } catch (interruptedexception e) { e.printstacktrace(); } clerk.consume(); } } } class producer implements runnable { clerk clerk; public producer(clerk clerk) { this.clerk = clerk; } public void run() { while(true) { try { thread.currentthread().sleep(10); } catch (interruptedexception e) { e.printstacktrace(); } clerk.addproduct(); } } } public class testmain { public static void main(string[] args) { clerk clerk = new clerk(); customer c1 = new customer(clerk); producer p1 = new producer(clerk); thread th2 = new thread(p1, "生产者"); thread th1 = new thread(c1, "消费者"); th2.start(); th1.start(); } }