Java提高——多线程(三)同步、锁
线程同步
线程的同步是为了防止多个线程访问同一个数据对象时,对数据造成破坏。
同步的方法就是使用synchronized关键字。
每个对象有且仅有一个同步锁,意味着同步锁依赖于对象而存在。每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法时,自动获得与当前执行的代码类的当前实例(this)有关的锁。当程序运行到synchronized方法时,就获得了该对象的同步锁。不同线程对同步锁是互斥的。
synchronized(obj){、、、、、}——>获得了obj对象的同步锁。
synchronized可以修饰方法和代码块,但是不能修饰构造器、成员变量等。
synchronized的规则:
—>当一个线程访问一个synchronized方法或代码块时,其他线程对该对象访问将被阻塞。
—>当一个线程访问一个synchronized方法或代码块时,其他线程可以访问该对象的非同步代码块
—>当一个线程访问一个synchronized方法或代码块时,其他线程对该对象的其他synchronized方法或代码块将被阻塞。
1、
public class SynchronizedTest { public static void main(String[] args) { Runnable demo = new demo1(); Thread t1 = new Thread(demo,"t1"); Thread t2 = new Thread(demo,"t2"); t1.start(); t2.start(); } static class demo1 implements Runnable{ @Override public void run(){ synchronized (this){ for (int i = 0; i < 10; i++) { System.out.print(Thread.currentThread().getName()+"-"+i+" "); } } } } }
2、
public class SynchronizedTest { public static void main(String[] args) { final demo1 demo = new demo1(); Thread t1 = new Thread(new Runnable() { @Override public void run() { demo.ru(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { demo.go(); } }, "t2"); t1.start(); t2.start(); } } class demo1{ public void ru(){ synchronized (this){ for (int i = 0; i < 10; i++) { System.out.print(Thread.currentThread().getName()+"-"+i+" "); } } } public void go(){ System.out.println("非同步方法"); } }
3、
public class SynchronizedTest { public static void main(String[] args) { final demo1 demo = new demo1(); Thread t1 = new Thread(new Runnable() { @Override public void run() { demo.ru(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { demo.go(); } }, "t2"); t1.start(); t2.start(); } } class demo1{ public void ru(){ synchronized (this){ for (int i = 0; i < 10; i++) { System.out.print(Thread.currentThread().getName()+"-"+i+" "); } } } public void go(){ synchronized (this) { System.out.println("非"); for (int i = 0; i < 10; i++) { System.out.print(Thread.currentThread().getName()+"-"+i+" "); } } } }
同步锁(Lock)
lock是控制多个线程对共享资源进行访问的工具。提供了对共享资源的独占访问,线程访问共享资源之前要先获得Lock对象 ,每次只能有一个对象对Lock对象枷锁。
实现线程控制中比较常用的是ReentrantLock(可重入锁),使用该锁可以显示的加锁、释放锁。
class ruo{ //获取锁对象 private final ReentrantLock loc = new ReentrantLock(); //...... public void re(){ //加锁 loc.lock(); try{ //需要保证线程安全的代码 //..... }finally { //释放锁 loc.unlock(); } } }
使用Lock与同步方法有点类似,只是Lock是显示的使用Lock对象作为同步锁,而使用同步方法是系统隐式的使用当前对象作为同步监视器。
ReentantLock具有可重入性,就是一个线程可以对已被加锁的ReentrantLock锁再次加锁。
死锁
当两个线程相互等待对方释放同步监视器的时候就会发生死锁。一旦发生死锁,程序不会发生任何异常也不会有任何提示,只是所有线程被阻塞,无法继续。
class A{ public synchronized void foo(B b){ System.out.println("foo方法"); b.last(); } public synchronized void last(){ System.out.println("a.last"); } } class B{ public synchronized void bar(A a){ System.out.println("bar方法"); a.last(); } public synchronized void last(){ System.out.println("b.last"); } } public class DeadLock implements Runnable{ A a = new A(); B b = new B(); public void init(){ Thread.currentThread().setName("主线程"); a.foo(b); } @Override public void run() { Thread.currentThread().setName("副线程"); b.bar(a); } public static void main(String[] args) { DeadLock deadLock = new DeadLock(); //以deadLock为target启动新的线程 new Thread(deadLock).start(); //调用init()方法 deadLock.init(); } }
程序发生死锁,没提醒,无异常。