JAVA 多线程机制(二)
主要内容
1.理解线程的并发性
2.线程的同步
3.线程的常用方法
上一章中由于线程的并发性导致了多线程的执行总是会出现一些问题。。线程的并发性是程序员不可控制
的,也是不可避免的,线程的并发性往往会导致问题的出现。。那么我们为什么要控制线程的并发性呢?比
如说:一个公子管理负责人正在修改雇员的工资表,而一些雇员正在领取工资,如果允许这样做必然会造成
混乱,因此,工资管理负责人正在修改工资表的时候,不允许任何的雇员领取工资,也就是说雇员们必须执行
等待状态。。
public class tickets { public static void main(String[] args) { sel s=new sel(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.start(); t2.start(); t3.start(); } } class sel implements Runnable { private int num=100; public void run() { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(num>0) { System.out.println(Thread.currentThread().getName()+"正在出售第"+num+"张票"+num--); }else { break; } } } }
依然是卖票问题,上述的线程由于线程的并发性而导致了卖票窗口出现了问题。。
为了解决这一问题,我们引入线程的同步。。
2.线程的同步
何为同步:我的理解就是:上述有三个线程,当t1线程启动的时候调用run()方法,那么同步函数也就起了作用,当t1执行run()
方法时,CPU分配给t1线程一把钥匙(并且只有一把),拿到这个钥匙之后开启synchronized函数的锁,然后进去执行里面的方法,
同时拿着钥匙,然后把门锁上,其他线程想访问这个函数的时候CPU没有钥匙,钥匙被线程t1拿进去了,所以其他的线程只能在线程池
中进行排队等待,当t1执行完毕后,把门打开,然后将钥匙还给CPU,然后t1线程或者消亡,或者进入阻塞状态,或者执行其他的过程
然后CPU再次把钥匙给在线程池中排队等待的线程,依次类推。。
(1)synchronized 同步函数
(2)synchronized 同步代码块
同步函数:
public class tickets { public static void main(String[] args) { sel s=new sel(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.start(); t2.start(); t3.start(); } } class sel implements Runnable { private int num=100; public synchronized void run()//同步函数的建立 { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(num>0) { System.out.println(Thread.currentThread().getName()+"正在出售第"+num+"张票"+num--); }else { break; } } } }
那么上面卖票中出现的问题就得到了解决,就不会出现一张票被卖多次,或者是出现卖第0张票的可能了。。
同步代码块:
public class tickets { public static void main(String[] args) { sel s=new sel(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.start(); t2.start(); t3.start(); } } class sel implements Runnable { private int num=100; public void run()//同步代码块的建立 { synchronized(this) { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(num>0) { System.out.println(Thread.currentThread().getName()+"正在出售第"+num+"张票"+num--); }else { break; } } } } }
同步代码块的实现也可以避免线程的并发性,并且同步代码块比同步函数更具有优势,同步代码块的
效率也是比同步函数要高的。最重要的是同步代码块的好处在于:
同步函数的锁只能是this,而同步代码块可以是任意一个obj对象,可以定义自己的类来定义自己的锁
同步代码块的速度也是比同步函数要更加快一些,应用的更加的广泛。。因此,在处理多线程安全问题的时候
最好使用同步代码块。。
线程的常用方法
1.start() 2.run() 3.sleep()4.isAlive()5.currentThread()6.interrupt()
前面的三个就不在进行介绍了。想必学习到了线程就知道这三个方法是什么意思了,isAlive()方法是判断
当前线程是否还活着。。currentThread()方法是获取当前执行线程的对象名,interrupt()方法是唤醒
休眠的线程。。下面举个例子。。。
/* * 比如说张小帅正在睡觉,那么正是上课时间 * 张小帅睡觉线程正在执行,但是在上课时的 * 老师这个线程看到了张小帅睡觉线程,然后 * 直接叫醒了张小帅线程。。。 * */ public class Demo_1_1 { public static void main(String[] args) { ClassRoom room=new ClassRoom(); room.students.start(); room.teacher.start(); } } class ClassRoom implements Runnable { Thread students,teacher; ClassRoom() { teacher=new Thread(this); students=new Thread(this); teacher.setName("张老师"); students.setName("张小帅"); } public void run() { if(Thread.currentThread()==students) { System.out.println(Thread.currentThread().getName()+"正在睡觉,不听课"); try { Thread.sleep(1000*60*60); } catch (InterruptedException e) { System.out.println(students.getName()+"被唤醒"); } System.out.println("开始听课"); } else if(Thread.currentThread()==teacher) { for(int i=1;i<=3;i++) { System.out.println("上课"); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } students.interrupt(); } } }
顺便提一嘴。。一个已经运行的线程在没有进入死亡状态时,不要再次分配实体,由于线程的分配只能引用
最后一个实体,那么先前的实体就会成为垃圾,并且垃圾回收站不会去进行回收,因为JVM会认为那个垃圾
实体正在运行,如果突然使其中断,那么必然会导致设备的损坏。。。
比如说:
Thread thread=new Thread(target);
thread.start();
如果再次执行下面的语句。。。
thread=new Thread(target);那么原本的实体就会成为垃圾。。。
就好比下面这样。。。。
因此在写多线程的时候一定要避免这样的问题发生。。。