多线程的实现步骤 :从开始创建的时候到线程的执行,最终到线程的终止
1)新建线程:没有执行资格,没有执行权
2)线程就绪:有执行资格,但没有执行权
(这里可能也会发生线程阻塞状态:线程睡眠或者等待)
3)线程执行:有执行资格并且有执行权
4)线程死亡:线程执行完毕,会被垃圾回收线程中的垃圾回收器及时的从内存中释放掉
当我们运行多线程模仿现实窗口售票应该怎么做?
例如:
使用实现Runnable方式
package com.westos.Runnable; public class MyRunnable implements Runnable{ /** * 同步锁的对象可以是任意的java类对象 * 例如我们在下方定义一个Dome类,并在此创建Dome类对象 * 然后将同步锁的对象换成Dome类对象,依旧可以模拟现实窗口售票 */ private Dome d=new Dome(); //定义一个私有公用的票数变量 private static int tickets=100; 定义同一把锁对象 //private Object obj=new Object(); @Override public void run() { while(true) { /** * 为了多个线程可以享用到共享资源我们必须的设置同一把锁,不然依旧会出现好几个窗口在出售同一张票的情况 * 这里的synchronized()方法就好比是一扇门,当其中的一个线程进来后就会关闭这扇门,然后去享受资源 * 等它享受完后,下一个线程会继承抢着进来享受资源,每一线程进来后 * 共享代码块就会发挥它的作用,这样就不会出现同票和负票的现象了 */ synchronized (d) { if(tickets>0) { //模范现实售票延迟的效果,给线程加入睡眠的功能(睡眠100毫秒) try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //因为上面是运用了实现Runnable接口的方法,不能直接调用getName方法,当你使用继承Thread类方式时,就可以使用了 //必须通过Thread类中的返回正在运行的线程的方法再用它去调用getName方法 System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票"); } } } } class Dome{ } }
创建一个测试类
package com.westos.Runnable; /** * 当我们模拟现实售票的情况时,会出现窗口出售同一张票的情况以及出现负票的情况 * (例如窗口1正在出售第0张票或者窗口1正在出售第-1张票的情况) *解决方法: * 将我们的共享数据用同步代码块包起来 * 格式synchronized(锁对象){ * 多条语句对共享数据的使用代码 * } * */ public class RunnableDome { public static void main(String[] args) { //创建资源类对象 MyRunnable my=new MyRunnable(); //创建Thread类对象 Thread t1=new Thread(my,"窗口1"); Thread t2=new Thread(my,"窗口2"); Thread t3=new Thread(my,"窗口3"); //执行线程 t1.start(); t2.start(); t3.start(); } }
当我们把同步锁放在方法中以及放在方法上时,依旧可以去模拟现实中窗口售票
切记:
当方法是静态时,记住要锁对象是该类名.class
例如:
package com.westos.Runnable; public class SellTicket implements Runnable{ //创建Dome类对象 Dome d=new Dome(); //定义一个通用的票数 private static int ticket=100; //定义一个变量 private int x=0; @Override public void run() { //循环 while(true) { if(x%2==0) { synchronized (SellTicket.class) {//当方法时静态时:锁对象:类名.class; 当方法不是静态时,锁对象是this if(ticket>0) { //让共享资源睡眠0.1秒 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } } else { sellTicket(); } x++; } } //我们可以将同步锁写在方法中,然后在run方法中调用即可 /*private void sellTicket() { synchronized (d) { if(ticket>0) { //让共享资源睡眠0.1秒 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } }*/ //或者我们可以直接把同步锁写在方法上去调用 //当该方法时静态的时候,该类中的锁对象是类名.class private synchronized static void sellTicket() { if(ticket>0) { //让共享资源睡眠0.1秒 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } class Dome{ } }
package com.westos.Runnable; public class SellTicketDome { public static void main(String[] args) { //创建资源类对象 SellTicket st=new SellTicket(); //创建三个Thread类对象 Thread t1=new Thread(st,"窗口1"); Thread t2=new Thread(st,"窗口2"); Thread t3=new Thread(st,"窗口3"); //启动线程 t1.start(); t2.start(); t3.start(); } } 运行结果: 窗口2正在出售第7张票 窗口3正在出售第6张票 窗口1正在出售第5张票 窗口1正在出售第4张票 窗口3正在出售第3张票 窗口3正在出售第2张票 窗口3正在出售第1张票
我们目前学习了线程安全的类有三个:
StringBuffer缓冲区、Vector集合、Hashtable集合
接下来让我们学习一种将线程不安全的类转换成线程安全类的方法
例如:
package com.westos.synchronizedList; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Vector; public class synchronizedListDome { public static void main(String[] args) { //目前我们学习过的线程安全的类有: StringBuffer sb=new StringBuffer(); Vector v=new Vector(); Hashtable ht=new Hashtable(); /** * 但是当我们运用一些集合的时候还是习惯用ArrayList集合 * 所以我们应该怎么将一些线程不安全的类转变成线程安全 的类呢 * 接下来就需要运用到collections工具类的synchronizedList方法了 */ //创建ArrayList集合对象,将类型定为String类型 ArrayList<String> list=new ArrayList<String>(); //调用方法://public static <T> List<T> synchronizedList(List<T> list) //返回是List集合,返回的集合就是线程安全的类了 List<String> array = Collections.synchronizedList(list); } }