实现多线程同步有两大方式——synchronized和lock
同步是指所有的线程不是一起进入到方法中执行,而是按照顺序一个一个进来的
一、
synchronized处理同步问题
1.同步代码块
使用同步代码块必须要设置一个要锁定的对象,一般为this
该方法进入到方法中的线程依然可能有多个,但是,进入到代码块中只能有一个线程
//使用同步代码块来实现多线程的同步 class MyThread implements Runnable { private int ticket = 10; @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<1000;i++) { //同一时刻,只允许一个线程进入代码块处理 synchronized (this) { //为程序逻辑上锁 if(this.ticket>0) //还有票 { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"还有"+this.ticket--+"张票"); } } } } } public class Test2 { public static void main(String[] args) { MyThread myThread=new MyThread(); Thread thread1=new Thread(myThread,"A"); Thread thread2=new Thread(myThread,"B"); Thread thread3=new Thread(myThread,"C"); thread1.setPriority(Thread.MIN_PRIORITY); thread2.setPriority(Thread.MAX_PRIORITY); thread3.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); thread3.start(); } }2.同步方法
同步方法可以保证数据的完整性,即线程安全,但是同步方法的执行速度会很慢
//同步方法 class MyThread implements Runnable { private int ticket = 100; @Override public void run() { // TODO Auto-generated method stub for(int i = 0;i < 100;i++) { this.sale(); } } public synchronized void sale() { if(this.ticket > 0) { try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"还有"+this.ticket--+"张票"); } } } public class Test2 { public static void main(String[] args) { MyThread mThread=new MyThread(); Thread thread1=new Thread(mThread,"A"); Thread thread2=new Thread(mThread,"B"); Thread thread3=new Thread(mThread,"C"); thread1.setPriority(Thread.MIN_PRIORITY); thread2.setPriority(Thread.MAX_PRIORITY); thread3.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); thread3.start(); } }
3.
synchronized来锁多对象
由于synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段。即synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。而当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。因此要想使用
synchronized来锁住多对象有两种方法,第一种为锁住同一个对象,第二种为锁住这个类对应的Class对象
(1)
synchronized锁住同一个对象
class Sync { public void Test() { synchronized (this) { System.out.println("test方法的开始,当前线程为"+Thread.currentThread().getName()); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("test方法结束,当前线程为"+Thread.currentThread().getName()); } } } class MyThread extends Thread { private Sync sync; public MyThread(Sync sync) { this.sync=sync; } public void run() { this.sync.Test(); } } public class Test2 { public static void main(String[] args) { Sync sync=new Sync(); for(int i=0;i<3;i++) { Thread thread=new MyThread(sync); thread.start(); } } }
(2)
synchronized锁住这个类对应的Class对象
用synchronized(Sync.class)实现了全局锁的效果。因此,如果要想锁的是代码段,锁住多个对象的同一方法,使用全局锁,锁的是类而不是this。
class Sync { public void Test() { synchronized (Sync.class) { System.out.println("test方法的开始,当前线程为"+Thread.currentThread().getName()); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("test方法结束,当前线程为"+Thread.currentThread().getName()); } } } class MyThread extends Thread { public void run() { Sync sync=new Sync(); sync.Test(); } } public class Test2 { public static void main(String[] args) { for(int i=0;i<3;i++) { Thread thread=new MyThread(); thread.start(); } } }static synchronized方法,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。
二、Lock锁解决同步
在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。相比之下使用Java提供的Lock对象,性能更高一些。
到了JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
到了JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
class MyThread implements Runnable { private int ticket = 100; private Lock ticketLock=new ReentrantLock(); @Override public void run() { // TODO Auto-generated method stub for(int i = 0;i<100;i++) { ticketLock.lock(); try { if (this.ticket > 0) { // 还有票 try { Thread.sleep(20); }catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket-- + " 张票"); } } finally { ticketLock.unlock(); } } } } public class Test2 { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread1= new Thread(myThread,"A"); Thread thread2= new Thread(myThread,"B"); Thread thread3= new Thread(myThread,"C"); thread1.setPriority(Thread.MIN_PRIORITY); thread2.setPriority(Thread.MAX_PRIORITY); thread3.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); thread3.start(); } }