java 中多线程的理解
jvm启动其实不止一个线程,它只少启动两个线,因为有一个是GC,另一个启动的程序。
意义在于提高效率。线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
线程的创建方式:
第一种方式 1、继承Thread 2、重写run方法,3、调用start()方法,这时jvm就会调用run方法。 多线程的随机性,who抢cpu到who执行,至于执行多长时间,cpu说了算。当然也可以人工去干预
为什么要重写run方法呢:线程有启动,有运行,运行什么呢,当然是运行run方法中的代码。
如果直接这样写
Thread thead=new Thead(); thead.start() ; 这样就没有运行的代码,没有意义,所以要重写run方法
如下案列面试经常会碰到
package cn.itcast.day5.thread;
public class BxdLessonOneThreadDemo {
public static void main(String[] args) {
Demo demo = new Demo();
//demo.start(); //如果这里不调用的话说明子线程没有运行,而只是运行一个普通的方法run(),所以这代码无论执行多少次都一样的结果。
demo.run(); //这样是普通调用方法,与线程没有关系。
for (int i = 0; i < 600; i++) {
System.out.println("main hello world--------" + i);
} } }
class Demo extends Thread {
@Override public void run() {
for (int i = 0; i < 600; i++) {
System.out.println("sub:demo run-----------" + i);
} } }
第二种方式:1,实现runnable接口,重写runnable run方法,通过thread建立子线程,然后再start()
开发中建议用这个,更面向对象,出时解决了java的单继承,而用接口来实现。
package cn.itcast.day5.thread; /** * 多窗口卖票窗口体现多线程 * * @author Administrator * */ public class MoreWindowSaleTicket { public static void main(String[] args) { // SaleTicket1(); SaleTicket(); } private static void SaleTicket1() { Ticket ticket0 = new Ticket(); Ticket ticket1 = new Ticket(); Ticket ticket2 = new Ticket(); Ticket ticket3 = new Ticket(); Ticket ticket4 = new Ticket(); ticket0.start(); ticket1.start(); ticket2.start(); ticket3.start(); ticket4.start(); } private static void SaleTicket() { TicketRunable ticketRunable = new TicketRunable(); Thread thread1 = new Thread(ticketRunable); Thread thread2 = new Thread(ticketRunable); Thread thread3 = new Thread(ticketRunable); Thread thread4 = new Thread(ticketRunable); thread1.start(); thread2.start(); thread3.start(); thread4.start(); } } // 第一种方式 class Ticket extends Thread { private static int count = 100;// 这里定义为静态也可以解决问题,但我们不这样这定义,一般会用另一种试,请参考SaleTicket(): public void run() { while (count > 0) { System.out.println(Thread.currentThread().getName() + "-------" + count--); } } } // 第二种方式 class TicketRunable implements Runnable { private static int count = 1000; @Override public void run() { while (true) // 这里会有个问题,如果count=1时.但cpu又执行另一个线程时可能导致count--=0 { synchronized (TicketRunable.class) //线程同步,共享资源部分加上同步,它做的事情是让别人等待,它进来,然后别人进来,它等待, //这样一直执行操作,至到完成。TicketRunable.class也称为锁,进来出去就得用这把锁。跟火车上的WC一样,有人,无人,这是标准我锁。呵呵。 //同步的前提是:必须是两个或是两个以上的线程才会加上同步锁,必须是多个线程使用同一个锁. //线程同步解决了线程的安全,但也有个不好的地方,就是耗资源,因为每次都要去检查锁。 { if (count > 0) { try { Thread.sleep(100);// 加上这个条件就有可能得到-1 0 -2 // 张票,所以得考虑线程的安全性问题,得到这些票的原因是 // 当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没有执行完,而另一个线程参与进来,共享数据就会被修改掉,这 // 就是线程的安全问题。 // 解决办法:多条语句共享数据的语句,让它执行完再执行另一个线程,其它线程不参与执行,虽然它已取得执行权,就是同步代表块 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-------:" + count--); } } } } }
如何找到多线程的问题
1、明确哪些代码是多线程运行的代码,2、明确哪些是共享数据,3、明确多线程运行代码中哪些语句是操作共享数据的。
同步函数与同步代码,可以用同步函数修改下上面的卖票案例
只要把同步代码重写一个同步方法就可以了 但问题又来了。同步一定要锁,但它的锁呢,如果函数被那个调用就是那个对象的字符码,也就是this. 所以得出结果一个同步函数中它的锁就是this
1 package cn.itcast.day5.thread; 2 3 /** 4 * 多窗口卖票窗口体现多线程 5 * 6 * @author Administrator 7 * 8 */ 9 public class MoreWindowSaleTicketSynchronizedFunction 10 { 11 public static void main(String[] args) 12 { 13 SaleTicket(); 14 } 15 16 private static void SaleTicket() 17 { 18 TicketRunable1 ticketRunable = new TicketRunable1(); 19 Thread thread1 = new Thread(ticketRunable); 20 Thread thread2 = new Thread(ticketRunable); 21 Thread thread3 = new Thread(ticketRunable); 22 Thread thread4 = new Thread(ticketRunable); 23 24 thread1.start(); 25 thread2.start(); 26 thread3.start(); 27 thread4.start(); 28 } 29 } 30 31 // 第二种方式 32 class TicketRunable1 implements Runnable 33 { 34 private static int count = 1000; 35 36 @Override 37 public void run() 38 { 39 while (true) // 这里会有个问题,如果count=1时.但cpu又执行另一个线程时可能导致count--=0 40 { 41 ShareSources(); 42 } 43 } 44 45 private synchronized void ShareSources() // 因为这部分是多线程操作的共享资源 46 // 由于同步都要用到锁,它的锁就是调用它的对象的字符码,也就是this 47 { 48 if (count > 0) 49 { 50 try 51 { 52 Thread.sleep(100);// 加上这个条件就有可能得到-1 0 -2 53 // 张票,所以得考虑线程的安全性问题,得到这些票的原因是 54 // 当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没有执行完,而另一个线程参与进来,共享数据就会被修改掉,这 55 // 就是线程的安全问题。 56 // 解决办法:多条语句共享数据的语句,让它执行完再执行另一个线程,其它线程不参与执行,虽然它已取得执行权,就是同步代表块 57 } 58 catch (InterruptedException e) 59 { 60 e.printStackTrace(); 61 } 62 System.out.println(Thread.currentThread().getName() + "-------:" + count--); 63 } 64 } 65 }
验证同步函数的锁是this(不能用static修饭)
package cn.itcast.day5.thread; /* * 用同步函数与同步代码来验证同步函数中的锁是this(调用这个访求的对象) */ public class UseDemoCheckThreadFunctionKeyIsKey { public static void main(String[] args) { Ticket1 ticket1 = new Ticket1(); Thread thread = new Thread(ticket1); Thread thread1 = new Thread(ticket1); thread.start(); ticket1.flag = false; thread1.start(); } } class Ticket1 implements Runnable { int count = 100; boolean flag = true; //Object object = new Object(); @Override public void run() { if (flag) { while (true) { synchronized (this) //这里的this就是这个对象,而同步函数中的也是this { if (count > 0) { try { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + ":" + count--); } catch (InterruptedException e) { e.printStackTrace(); } } } } } else { while (true) { show(); } } } public synchronized void show() { if (count > 0) { try { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + ":" + count--); } catch (InterruptedException e) { e.printStackTrace(); } } } }
那静态方法中的锁是什么呢 就是那个类的字节码对象
package cn.itcast.day5.thread; /* * 用同步函数与同步代码来验证同步函数中的锁是this(调用这个访求的对象) */ public class UseDemoCheckThreadFunctionKeyIsKey { public static void main(String[] args) { Ticket1 ticket1 = new Ticket1(); Thread thread = new Thread(ticket1); Thread thread1 = new Thread(ticket1); thread.start(); ticket1.flag = false; thread1.start(); } } class Ticket1 implements Runnable { static int count = 100; boolean flag = true; Object object = new Object(); @Override public void run() { if (flag) { while (true) { synchronized (Ticket1.class) //由于是同步方法是静态的,那它的锁的是这个方法所以对象的字节码对象,而这里不能用this, //因为静态中没有this,所以要用字节码才能线程安全。 { if (count > 0) { try { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + ":" + count--); } catch (InterruptedException e) { e.printStackTrace(); } } } } } else { while (true) { show(); } } } public static synchronized void show() { if (count > 0) { try { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + ":" + count--); } catch (InterruptedException e) { e.printStackTrace(); } } } }
由于单例中懒汉式在多线程中使用同步锁就会又程序效率降低,又一个好办法可以解决这个问题如下
// 延迟加载的同步懒汉式写法 class Single { private static Single single = null; private Single() { } public static Single getInstance() { if (single == null) { synchronized (Single.class) // 线程A进来,休息一会,其它线程进不来,等它实例化后其它线和进来,发现single不为空了。 // 就是不会在实例化了,这样就提高了程序的运行率, 所以面试一般会面试这个,便实际开发中最好用饿汉式如下 { if (single == null) { single = new Single(); } } } return single; } } //饿汉式 class SingleHugry { private static SingleHugry singleHugry = null; private SingleHugry() { singleHugry = new SingleHugry(); } public static SingleHugry getInstance() { return singleHugry; } }
死锁:A有自己的锁,但要进B内,B有锁,要进A内,这样就会造成死锁:一般出现在多线程同步中,多个锁出现在一个方法中。
1 package cn.itcast.day5.thread; 2 3 public class 死锁 4 { 5 @SuppressWarnings("static-access") 6 public static void main(String[] args) throws InterruptedException 7 { 8 Ticket2 ticket2 = new Ticket2(); 9 Thread thread1 = new Thread(ticket2); 10 Thread thread2 = new Thread(ticket2); 11 thread1.start(); 12 13 thread1.sleep(10); 14 15 ticket2.flag = false; 16 thread2.start(); 17 } 18 } 19 20 class Ticket2 implements Runnable 21 { 22 int count = 1000; 23 boolean flag = true; 24 Object object = new Object(); 25 26 @Override 27 public void run() 28 { 29 if (flag) 30 { 31 while (true) 32 { 33 synchronized (object) 34 { 35 show(); 36 } 37 38 } 39 } else 40 { 41 while (true) 42 { 43 show(); 44 } 45 } 46 } 47 48 public synchronized void show()// this 49 { 50 synchronized (object) 51 { 52 if (count > 0) 53 { 54 try 55 { 56 Thread.sleep(10); 57 System.out.println(Thread.currentThread().getName() + "....:" + count--); 58 } 59 catch (InterruptedException e) 60 { 61 e.printStackTrace(); 62 } 63 } 64 } 65 66 } 67 }
posted on 2013-05-13 20:39 peter.peng 阅读(526) 评论(0) 编辑 收藏 举报