方式一:同步代码块
class MyRunable03 implements Runnable{ private int ticket = 10; @Override public void run() { for (int i = 0; i < 100; i++) { synchronized (this){ ticket--; if(ticket>=0){ System.out.println(ticket); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
这个例子使用 synchronized() { } 同步代码块的方法。
- synchronized (this) , this表示这个类, 而且
MyRunable03 runable03 = new MyRunable03(); 只 new 一个实例。同一个对象,才能锁住。
- 在 同步代码块里 再一次做 if(ticket>=0) 有效性检查,非常有必要。
-
在 同步代码块里 做延时 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
是为了模拟实际的业务功能花费的时间。
- 使用synchronized 关键字的效果是,每次只允许一个线程访问。保证线程安全的同时,也降低效率,相当于一个单线程在工作。
测试
public static void main(String[] args) { MyRunable03 runable03 = new MyRunable03(); Thread t1 = new Thread(runable03); Thread t2 = new Thread(runable03); t1.start(); t2.start(); }
注意事项:
同步代码块里边的内容尽量少,这样就减少性能损失。(synchronized 锁会降低程序执行的效率。)
class MyRunable03 implements Runnable{ private int ticket = 10; @Override public void run() { for (int i = 0; i < 100; i++) { method(); } } private synchronized void method(){ this.ticket--; if(this.ticket>=0){ System.out.println(this.ticket); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
同步方法用到的 同步监视器 synchronized , 锁住的是 这个类 。
方式三:互斥锁(ReentrantLock)
class MyRunable03 implements Runnable{ private int ticket = 10; @Override public void run() { for (int i = 0; i < 100; i++) { method(); } } ReentrantLock lock = new ReentrantLock(); private void method(){ try { lock.lock(); this.ticket--; if(this.ticket>=0){ System.out.println(this.ticket); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }finally { lock.unlock(); } } }
小结
-
同步代码块的代码量要尽量少。
-
同步代码块里边,避免阻塞。比如 InputStream.read().
-
同步代码块里边, 不要调用 其他对象的 同步方法(也有synchronized)。免得锁不能正常释放,导致死锁。
死锁
共享资源需要线程同步,过多的同步,会导致死锁。