Java多线程之线程安全与同步实例

时间:2021-08-11 18:41:57

1.1    线程安全与同步实例

1.1.1  购票同步对象锁

/*

 * 用程序模拟铁路售票系统:实现通过两个售票点发售某日某次列车的50张车票,

 * 一个售票点用一个线程表示

 */

publicclass SyncDemo {

 

         public static void main(String[] args) {

 

                   /*

                    * new SaleTicketThread("窗口1").start(); new

                    * SaleTicketThread("窗口2").start();

                    */

 

                   // 创建票对象(内部封装了卖票的方法,并且具备运行在线程中的能力)

                   Ticket tickt = new Ticket();

                   // 让多个窗口同时卖票(让线程执行指定的任务对象)

                   new Thread(tickt, "窗口1").start();

                   new Thread(tickt, "窗口2").start();

         }

 

}

 

classTicket implements Runnable {

 

         int num = 50;// 票数

         Object obj = new Object();

 

         @Override

         public void run() {

                   // 不停地卖票

                   while (true) {

                            sale();

                   }

         }

 

         /**

          * 卖票,每调用一次,卖一张票

          */

         //同步方法,其实就是同步代码块的简写方式

         public synchronized void sale() {

                   // 同步代码块,同一时间只能有一个线程进行执行里面的代码

//               synchronized (this) {// 同步锁,每个对象都可以作为同步锁进行使用(可用任何对象)

                            if (num > 0) {

                                     try {

                                               Thread.sleep(100);

                                               System.out.println(Thread.currentThread().getName()

                                                                 +"...sale..." + num--);

                                     } catch(InterruptedException e) {

                                               //TODO Auto-generated catch block

                                               e.printStackTrace();

                                     }

                            }

//               }

         }

 

}

classSaleTicketThread extends Thread {

 

         private int num = 50;

 

         public SaleTicketThread(String name) {

                   super(name);

         }

 

         @Override

         public void run() {

                   while (num > 0) {

                            System.out.println(getName()+ "......sale....." + num);

                            num--;

                   }

         }

 

}

1.1.2  同账户同步存钱

publicclass Ex1 {

 

         public static void main(String[] args){

                   Account account = newAccount();

                   new Thread(account, "小强").start();

                   new Thread(account, "小花").start();

 

         }

 

}

 

classAccount implements Runnable{

 

         private int money = 0;  //存款

        

         @Override

         public void run() {

                   //存钱三次,每次存100元,并且在读完之后显示账户的余额

                   for(int i=0;i<3;i++){

                            saveMoney(100);

                   }

         }

        

         /**

          * 存钱

          */

         public synchronized void saveMoney(intmoney){

                   this.money +=money;

                   System.out.println(Thread.currentThread().getName()+"存入100元,账户余额为"+this.money);

                   try {

                            Thread.sleep(500);

                   } catch (InterruptedExceptione) {

                            // TODOAuto-generated catch block

                            e.printStackTrace();

                   }

         }       

}

1.1.3  火车过山洞

/*

 * 有5辆火车要过山洞,但确保山洞同时只能有一辆火车通过(过山洞需要1秒),打印输出火车通过的顺序。

 * (过山洞的顺序是不可控的,只要保证同一时间只有一辆火车能通过山洞即可)

 * 提示:使用线程同步,一辆火车就是一个线程

 */

publicclass Ex2 {

 

         public static void main(String[] args){

                  

                   new Train("火车1").start();

                   new Train("火车2").start();

                   new Train("火车3").start();

                   new Train("火车4").start();

                   new Train("火车5").start();

         }

}

 

classTrain extends Thread{

        

         public Train(String name){

                   super(name);

         }

         @Override

         public void run() {

                   synchronized (Train.class) {

                            System.out.println(getName()+"过山洞.....");

                            try {

                                     Thread.sleep(1000);

                            } catch(InterruptedException e) {

                                     // TODOAuto-generated catch block

                                     e.printStackTrace();

                            }

                   }

         }

}

1.1.4  处理单例模式中的线程同步问题

在懒汉式单例模式中存在线程同步问题

处理方案:采用双重判断的形式同时解决效率和线程安全的问题

 

publicclass Demo {

 

         public static void main(String[] args){

                   new Thread() {

 

                            @Override

                            public void run() {

                                     System.out.println(Singleton3.getInstance());

                            }

 

                   }.start();

 

                   new Thread() {

 

                            @Override

                            public void run() {

                                     System.out.println(Singleton3.getInstance());

                            }

 

                   }.start();

 

         }

 

}

 

// 单例模式(饿汉式)

classSingleton {

 

         //静态字段是在类加载的时候就初始化了

         private static Singleton instance = newSingleton();

 

         // 私有化构造方法,防止外界创建对象

         private Singleton() {

 

         }

 

         public static Singleton getInstance() {

                   return instance;

         }

}

 

// 单例模式(懒汉式)

// 存在线程安全问题,可以通过双重判断加同步处理解决这里的线程安全问题

 

classSingleton2 {

 

         private static Singleton2 instance;

 

         private Singleton2() {

                   // emty

         }

 

         // 在这里,线程安全问题只会出现在第一次创建对象的时候,只要对象已经被创建完,

         // 那么就不需要再对代码进行同步处理

         // public static synchronizedSingleton2 getInstance(){//存在线程安全问题,可以通过双重判断加同步处理解决这里的线程安全问题

         public static Singleton2 getInstance(){

 

                   if (instance == null) {        //通过双重判断对象是否存在

                            synchronized(Singleton2.class) {//同步

                                     if(instance == null) {

                                               try{

                                                        Thread.sleep(100);

                                               }catch (InterruptedException e) {

                                                        //TODO Auto-generated catch block

                                                        e.printStackTrace();

                                               }

                                               instance= new Singleton2();

                                     }

                            }

 

                   }

                   return instance;

         }

}

 

/*使用静态内部类实现单例模式

 * 在类加载的时候会加载类中的成员,会初始化静态字段,将类中的成员加载到内存中(方法区)

 *

 */

classSingleton3{

        

         private Singleton3(){

                  

         }

        

         //静态代码块只会在类加载的时候被调用一次

         static{

                   System.out.println("外部类中的静态代码块......");

         }

        

         //静态内部类

         private static class Inner{

                   static Singleton3 instance =new Singleton3();

                   static{

                            System.out.println("内部类中的静态代码块.....");

                   }

         }

        

         public static Singleton3 getInstance(){

                   return Inner.instance;

         }

}

 

1.1.5  数字和字母的间隔输出

/**

 * 1.         写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z打印顺序为12A34B56C……5152Z(2个数字1个字母)。

       提示:使用线程间的通信。

 */

publicclass Ex1 {

 

         public static void main(String[] args){

                   // TODO Auto-generated methodstub

                   Printer printer = newPrinter();

                   newNumberThread(printer).start();

                   newLetterThread(printer).start();

         }

 

}

//数字输出线程

classNumberThread extends Thread{

        

         private Printer printer;

         public NumberThread(Printer printer){

                   this.printer = printer;

         }

        

         @Override

         public void run() {

                   for(int i=1;i<=52;i++){

                            printer.printNum(i);

                   }

         }

}

 

//字母输出线程

classLetterThread extends Thread{

        

         private Printer printer;

        

         public LetterThread(Printer printer){

                   this.printer = printer;

         }

        

         @Override

         public void run() {

                   for(charc='A';c<='Z';c++){

                            printer.printLetter(c);

                   }

         }

}

/**

 * 打印输出类

 */

classPrinter{

        

         private boolean numOut = false;  //信号量,记录数字是否已经输出

         //输出数字

         public synchronized void printNum(intnum){

                   try {

                            if(numOut){//如果数字已经输出,就等待输出字母

                                     wait();//注意:wait、notify的调用者必须是当前同步代码块对应的同步锁

                            }

                            System.out.println(num);  //输出数字

                            //如果刚刚输出的数字是偶数的话,就唤醒字母输出线程

                            if(num % 2 == 0){

                                     numOut =true;  //标记已经输出数字

                                     notify();       //唤醒字母输出线程去输出字母

                            }

                            Thread.sleep(200);

                   } catch (InterruptedExceptione) {

                            // TODOAuto-generated catch block

                            e.printStackTrace();

                   }

                  

         }

        

         //输出字母

         public synchronized voidprintLetter(char c){

                   try {

                            if(!numOut){    //如果还没有输出数字,就等待数字输出

                                     wait();

                            }

                            System.out.println(c);   //输出字母

                            numOut = false;       //标记已经输出国字母,等待输出数字

                            notify();             //唤醒数字输出线程去输出数字

                            Thread.sleep(200);

                   } catch (InterruptedExceptione) {

                            // TODOAuto-generated catch block

                            e.printStackTrace();

                   }

                  

         }

}

 

1.1.6  主子线程的循环输出

/**

 * 子线程循环3次,接着主线程循环6次,接着又回到子线程循环3次,接着再回到主线程又循环6次,如此循环10次,请写出程序

 */

publicclass Ex2 {

 

         static boolean flag = false; // 记录子线程是否已经输出

         static Object lock = new Object();

 

         public static void main(String[] args){

                   // TODO Auto-generated methodstub

                   // 子线程

                   new Thread() {

                            @Override

                            public void run() {

                                     try {

                                               for(int i = 1; i <= 10; i++) {

                                                        synchronized(lock) {

                                                                 if(flag) {

                                                                           lock.wait();

                                                                 }

                                                                 System.out.println("~~~~~~~~~~~~~~第" + i

                                                                                    +"回合~~~~~~~~");

                                                                 for(int j = 1; j <= 3; j++) {

                                                                           System.out.println("子线程" + j);

                                                                           Thread.sleep(200);

                                                                 }

                                                                 flag= true;

                                                                 lock.notify();

                                                        }

                                               }

                                     } catch(InterruptedException e) {

                                               //TODO Auto-generated catch block

                                               e.printStackTrace();

                                     }

                            };

                   }.start();

                   // 主线程

                   for (int i = 1; i <= 10;i++) {

                            try {

                                     synchronized(lock) {

                                               if(!flag){

                                                        lock.wait();

                                               }

                                               for(int j = 1; j <= 6; j++) {

                                                        System.out.println("主线程....." + j);

                                                        Thread.sleep(200);

                                               }

                                               flag= false;

                                               lock.notify();

                                     }

                            } catch(InterruptedException e) {

                                     // TODOAuto-generated catch block

                                     e.printStackTrace();

                            }

                   }

         }

 

}