针对两种线程创建方式,有两种不同的同步方式:关键字synchronized:
同步代码块:两种线程创建方式的不同在于对于继承类的线程,同步监视器说要用到的对象必须声明为static,对于实现Runnable接口的线程,可以直接使用this当作同步监视器
同步方法:Runnable接口型,将共享数据的代码块单独拿出来声明为一个方法,将此方法加上synchronized关键字即可,对于继承型,同样声明为方法,不过此方法要声明为static
1 package ThreadTest01; 2 3 /**对于创建实现Runnable接口的线程 4 * 1:问题:卖票过程中出现了错票,重票情况,出现了线程的安全问题 5 * 2:原因:当某个进程尚未操作完成时,另一个进程参与进来也进行操作 6 * 3:解决:当一个线程a在操作ticket时,其他线程不可以参与进来,等到a操作ticket结束,其他线程才可以进入 7 * 即使a出现了堵塞,其它线程也不可以进入 8 * 4:在java中,使用同步机制解决线程安全问题 9 * 10 * 方式一:同步代码块: 11 * 关键字synchronized(同步监视器){ 12 * //需要被同步的代码 13 * } 14 * 说明:操作共享数据的代码即为需要被同步的代码 15 * 共享数据:多个线程共同操作的变量 eg:ticket 16 * 同步监视器:俗称'锁'。任何一个类的对象都可以充当锁, 17 * 要求多个线程必须共享同一个锁,也可以用this来代替,不需要重新创建对象,是最方便的方法 18 * 因为run方法属于这个类,这个类只创建了一个对象demo 19 ************************************************************************* 20 * 对于通过创建子类继承Thread类的线程:也可以使用同步代码块,方式一样 21 * synchronized(同步监视器){ 22 * * //需要被同步的代码 23 * } 24 * 不一样的在于,创建同步监视器(锁)时,需要声明为static类变量保证创建的多个对象对应的线程都可以共享此锁 25 * static Object obj=new Object(); 26 * 不可以用this代替,因为对象不唯一 27 * 28 * 方式二:同步方法。 29 * 如果操作共享数据的代码完整的在一个方法中, 30 * 实现Runnable接口的线程:可以将此方法创建声明为 private synchronized void Methods(); 31 * 子类继承Thread类的线程:将此方法创建声明为 private static synchronized void Methods(); 32 * 同步方法依旧涉及到同步监视器,只是对于 33 * 非静态的同步方法,同步监视器时this 34 * 静态的同步方法,同步监视器是当前类对象(涉及到反射) 35 * 5:同步的方式解决的线程的安全问题,但是相当于一个单线程的过程,效率低 36 ************************************************************************ 37 */ 38 public class Demo implements Runnable{ 39 private int ticket = 100; 40 // Object obj=new Object();//充当锁 41 @Override 42 public void run() { 43 while (true) { 44 synchronized (this){//使用this,指代demo因为时唯一的对象 45 if (ticket > 0) { 46 try { 47 Thread.sleep(100); 48 } catch (InterruptedException e) { 49 e.printStackTrace(); 50 } 51 System.out.println(Thread.currentThread().getName() + "\t" + ticket); 52 ticket--; 53 } else { 54 break; 55 } 56 } 57 } 58 } 59 60 61 public static void main(String[] args) { 62 Demo demo = new Demo();//唯一的对象 63 Thread thread = new Thread(demo); 64 Thread thread1 = new Thread(demo); 65 Thread thread2 = new Thread(demo); 66 thread.setName("线程0"); 67 thread1.setName("线程1"); 68 thread2.setName("线程2"); 69 thread.start(); 70 thread1.start(); 71 thread2.start(); 72 } 73 }
补充:第三种解决线程安全问题的方法:Lock锁,JDK5.0新增
1 /** 2 * 3 *解决线程安全方式3:Lock锁,JDK5.0新增 4 * 5 * 6 * 7 * 8 */ 9 10 public class Demo implements Runnable{ 11 private int ticket = 100; 12 private ReentrantLock lock=new ReentrantLock(); 13 @Override 14 public void run() { 15 while (true){ 16 try { 17 lock.lock();//调用lock方法 18 if (ticket > 0) { 19 try { 20 Thread.sleep(100); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 System.out.println(Thread.currentThread().getName() + "\t" + ticket); 25 ticket--; 26 } 27 }finally { 28 lock.unlock();//调用unlock方法 29 } 30 } 31 } 32 33 public static void main(String[] args) { 34 Demo demo = new Demo();//唯一的对象 35 Thread thread = new Thread(demo); 36 Thread thread1 = new Thread(demo); 37 Thread thread2 = new Thread(demo); 38 thread.setName("线程0"); 39 thread1.setName("线程1"); 40 thread2.setName("线程2"); 41 thread.start(); 42 thread1.start(); 43 thread2.start(); 44 } 45 }