#Thread synchronization
When calling the same object between multiple threads, for the safety and accuracy of operation, the object needs to be synchronized to ensure that the result of the object is correct when used by each thread, and the status of the object is Reasonably, this part involves knowledge points such as synchronization and thread locks. This part only involves the concepts of synchronized and synchronization lock (Lock).synchronized
The synchronized keyword can modify objects and methods. The usual usage is as follows://同步代码块 synchronized(Object object){ ... } //或者 //同步方法 public synchronized void test(){ ... }There is a concept of synchronization monitor, such as the above The object object of the synchronized code block and the this object of the synchronized method will be monitored synchronously. When multiple threads call a synchronized code block or method at the same time, only one thread can obtain the synchronously monitored object lock at any time. After executing the code The lock will not be released until later. During this period, other calling threads can only wait for the lock to be released before calling. The sell method in the SellRunnable class mentioned above also uses synchronized. The code above executes too fast, so it cannot be sensed. If you modify it, you can understand the difference between synchronized and not.
public class ThreadTest { public static void main(String[] args) { SellRunnable sellRunnable = new SellRunnable(); Thread thread1 = new Thread(sellRunnable, "1"); Thread thread2 = new Thread(sellRunnable, "2"); Thread thread3 = new Thread(sellRunnable, "3"); thread2.start(); thread1.start(); thread3.start(); } } class SellRunnable implements Runnable { //有十张票 int index = 10; public void sell() { if (index >= 1) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } index--; System.out.println("售货窗口:" + Thread.currentThread().getName() + " 卖出了一张票,剩余: " + index); } else { System.out.println("售货窗口:" + Thread.currentThread().getName() + " 买票时没票了"); } } @Override public void run() { while (index > 0) { System.out.println("售货窗口:" + Thread.currentThread().getName() + " 开始买票"); sell(); } } } //执行结果: 售货窗口:1 开始买票 售货窗口:2 开始买票 售货窗口:3 开始买票 售货窗口:2 卖出了一张票,剩余:9 售货窗口:2 开始买票 售货窗口:1 卖出了一张票,剩余:9 售货窗口:1 开始买票 售货窗口:3 卖出了一张票,剩余:8 售货窗口:3 开始买票 售货窗口:1 卖出了一张票,剩余:6 售货窗口:1 开始买票 售货窗口:2 卖出了一张票,剩余:6 售货窗口:2 开始买票 售货窗口:3 卖出了一张票,剩余:5 售货窗口:3 开始买票 售货窗口:1 卖出了一张票,剩余:4 售货窗口:1 开始买票 售货窗口:2 卖出了一张票,剩余:3 售货窗口:3 卖出了一张票,剩余:2 售货窗口:3 开始买票 售货窗口:2 开始买票 售货窗口:3 卖出了一张票,剩余:1 售货窗口:2 卖出了一张票,剩余:0 售货窗口:1 卖出了一张票,剩余:1 Process finished with exit code 0 //可以看到,票数减少是错误的 //sell方法添加synchronized修饰符后 执行结果: public synchronized void sell() { if (index >= 1) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } index--; System.out.println("售货窗口:" + Thread.currentThread().getName() + " 卖出了一张票,剩余: " + index); } else { System.out.println("售货窗口:" + Thread.currentThread().getName() + " 买票时没票了"); } } 售货窗口:2 开始买票 售货窗口:3 开始买票 售货窗口:1 开始买票 售货窗口:2 卖出了一张票,剩余:9 售货窗口:2 开始买票 售货窗口:1 卖出了一张票,剩余:8 售货窗口:1 开始买票 售货窗口:3 卖出了一张票,剩余:7 售货窗口:3 开始买票 售货窗口:1 卖出了一张票,剩余:6 售货窗口:1 开始买票 售货窗口:2 卖出了一张票,剩余:5 售货窗口:2 开始买票 售货窗口:1 卖出了一张票,剩余:4 售货窗口:1 开始买票 售货窗口:1 卖出了一张票,剩余:3 售货窗口:1 开始买票 售货窗口:3 卖出了一张票,剩余:2 售货窗口:3 开始买票 售货窗口:1 卖出了一张票,剩余:1 售货窗口:1 开始买票 售货窗口:1 卖出了一张票,剩余:0 售货窗口:2 买票时没票了 售货窗口:3 买票时没票了 Process finished with exit code 0 // 可以看到,票数是正常减少的After the above synchronization of the sell method, at a certain moment, only one thread will call this method, so the result obtained when judging the index is the correct result. During the above synchronization, thread safety is ensured by reducing operating efficiency. For this reason, do not synchronize the unnecessary methods and objects in the thread class, and only synchronize the resources or objects with competition. The code is synchronized. After synchronization identification, the following points can release the lock: Code block, method execution is completed (normal completion, return or break, exception thrown) Call The wait method is used to pause the current thread. When a thread executes a synchronized code block, the sleep and yield methods will not release the synchronization lock, nor will the suspend method (try to avoid using suspend and resume to manipulate the thread state during thread operation, which is easy Leading to deadlock.)
Synchronized Lock
The synchronized mentioned above is a keyword in java, and it is also mentioned in When sleeping or performing IO operations, the thread will not release the thread lock, and other threads need to wait. This sometimes reduces the execution efficiency, so an alternative that can release the thread lock when the thread is blocked is needed. Lock It appeared just to solve this problem. Lock is a class in java, in the java.util.concurrent.locks package, the specific code is as follows:public interface Lock { void lock();//加锁 void lockInterruptibly() throws InterruptedException;//加锁 boolean tryLock();//加锁 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//加锁 void unlock();//释放锁 Condition newCondition();//线程协作中用到 }An implementation subclass of the Lock interface is ReentrantLock, in java. Under the util.concurrent.locks package, the source code of ReentrantLock is as follows:
public class ReentrantLock implements Lock, Serializable { private static final long serialVersionUID = 7373984872572414699L; private final ReentrantLock.Sync sync; public ReentrantLock() { this.sync = new ReentrantLock.NonfairSync(); } public ReentrantLock(boolean var1) {//是否创建公平锁 this.sync = (ReentrantLock.Sync)(var1?new ReentrantLock.FairSync():new ReentrantLock. NonfairSync()); } public void lock() { this.sync.lock(); } public void lockInterruptibly() throws InterruptedException { this.sync.acquireInterruptibly(1); } public boolean tryLock() { return this.sync.nonfairTryAcquire(1); } public boolean tryLock(long var1, TimeUnit var3) throws InterruptedException { return this.sync.tryAcquireNanos(1, var3.toNanos(var1)); } public void unlock() { this.sync.release(1); } public Condition newCondition() { return this.sync.newCondition(); } public int getHoldCount() {//当前线程持有该锁的数量 return this.sync.getHoldCount(); } public boolean isHeldByCurrentThread() {//该锁是否被当前线程持有 return this.sync.isHeldExclusively(); } public boolean isLocked() {//是否被其他线程持有该锁 return this.sync.isLocked(); } public final boolean isFair() {//是否是公平锁 return this.sync instanceof ReentrantLock.FairSync; } protected Thread getOwner() {//当前锁的持有线程 return this.sync.getOwner(); } public final boolean hasQueuedThreads() {//是否有线程在等待该锁 return this.sync.hasQueuedThreads(); } public final boolean hasQueuedThread(Thread var1) {//目标线程是否在等待该锁 return this.sync.isQueued(var1); } public final int getQueueLength() {//等待该锁线程的数量 return this.sync.getQueueLength(); } protected Collection<Thread> getQueuedThreads() {//获取所有等待该锁的线程集合 return this.sync.getQueuedThreads(); } ... }
How to use Lock
lock lock() is used to acquire the lock. If the lock is occupied by other threads, it will wait.public class LockTest { public static void main(String[] args) { com.test.java.SellRunnable sellRunnable = new com.test.java.SellRunnable(); Thread thread1 = new Thread(sellRunnable, "1号窗口"); Thread thread2 = new Thread(sellRunnable, "2号窗口"); Thread thread3 = new Thread(sellRunnable, "3号窗口"); thread1.start(); thread2.start(); thread3.start(); } }
public class SellRunnable implements Runnable { //有十张票 int index = 10; Lock lock = new ReentrantLock(); public void sell() { try { lock.lock(); System.out.println("售货柜台:" + Thread.currentThread().getName() + "获取了票源+++++"); if (index >= 1) { index--; System.out.println("售货柜台:" + Thread.currentThread().getName() + "卖出了一张票,剩余: " + index); } else { System.out.println("售货柜台:" + Thread.currentThread().getName() + "买票时没票了000"); } } finally { lock.unlock(); } } @Override public void run() { while (index > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } }Run result:
售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:9 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:8 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:7 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:6 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:5 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:4 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:3 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:2 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:1 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:0 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口买票时没票了000 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口买票时没票了000 Process finished with exit code 0 //每一个窗口都随机获取票源、然后卖出票tryLocktryLock() tries to acquire the lock. If the acquisition is successful, it returns true. If it fails, it returns false and will not enter the waiting state.
public class SellRunnable implements Runnable { //有十张票 int index = 10; Lock lock = new ReentrantLock(); public void sell() { if (lock.tryLock()) { try { System.out.println("售货柜台:" + Thread.currentThread().getName() + "获取了票源+++++"); if (index >= 1) { index--; System.out.println("售货柜台:" + Thread.currentThread().getName() + "卖出了一张票,剩余:" + index); } else { System.out.println("售货柜台:" + Thread.currentThread().getName() + "买票时没票了000"); } } finally { lock.unlock(); } } else { System.out.println("售货柜台:" + Thread.currentThread().getName()+"没有获取票源!!!"); } } @Override public void run() { while (index > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } }Running result:
售货柜台:1号窗口获取了票源+++++ 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口没有获取票源!!! 售货柜台:1号窗口卖出了一张票,剩余:9 售货柜台:2号窗口没有获取票源!!! 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:8 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:7 售货柜台:1号窗口没有获取票源!!! 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:6 售货柜台:1号窗口获取了票源+++++ 售货柜台:2号窗口没有获取票源!!! 售货柜台:3号窗口没有获取票源!!! 售货柜台:1号窗口卖出了一张票,剩余:5 售货柜台:2号窗口获取了票源+++++ 售货柜台:1号窗口没有获取票源!!! 售货柜台:2号窗口卖出了一张票,剩余:4 售货柜台:3号窗口没有获取票源!!! 售货柜台:1号窗口获取了票源+++++ 售货柜台:2号窗口没有获取票源!!! 售货柜台:3号窗口没有获取票源!!! 售货柜台:1号窗口卖出了一张票,剩余:3 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:2 售货柜台:2号窗口获取了票源+++++ 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口卖出了一张票,剩余:1 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:0 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口没有获取票源!!! Process finished with exit code 0//没有获取到货源的票口,就直接没有等待,进入下次买票tryLock(long time, TimeUnit unit)tryLock(long time, TimeUnit unit) can be set to wait for a period of time when the lock cannot be obtained. . //The first parameter is always long, the second parameter time unit
public class SellRunnable implements Runnable { //有十张票 int index = 10; Lock lock = new ReentrantLock(); public void sell() { try { if (lock.tryLock(1000, TimeUnit.MILLISECONDS)) { try { System.out.println("售货柜台:" + Thread.currentThread().getName() + "获取了票源+++++"); if (index >= 1) { index--; System.out.println("售货柜台:" + Thread.currentThread().getName() +"卖出了一张票,剩余:" + index); } else { System.out.println("售货柜台:" + Thread.currentThread(). getName() + "买票时没票了000"); } try { Thread.sleep(2000);//人为加入买票时间 } catch (InterruptedException e) { e.printStackTrace(); } } finally { lock.unlock(); } } else { System.out.println("售货柜台:" + Thread.currentThread().getName() + "没有获取票源!!!"); } } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { while (index > 0) { try { Thread.sleep(500);//要不执行太快,看不出效果 } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } }Execution result:
售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:9 售货柜台:2号窗口没有获取票源!!! 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:8 售货柜台:3号窗口没有获取票源!!! 售货柜台:1号窗口没有获取票源!!! 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:7 售货柜台:1号窗口没有获取票源!!! 售货柜台:2号窗口没有获取票源!!! 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:6 售货柜台:2号窗口没有获取票源!!! 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:5 售货柜台:3号窗口没有获取票源!!! 售货柜台:1号窗口没有获取票源!!! 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:4 售货柜台:1号窗口没有获取票源!!! 售货柜台:2号窗口没有获取票源!!! 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:3 售货柜台:2号窗口没有获取票源!!! 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:2 售货柜台:3号窗口没有获取票源!!! 售货柜台:1号窗口没有获取票源!!! 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:1 售货柜台:1号窗口没有获取票源!!! 售货柜台:2号窗口没有获取票源!!! 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:0 售货柜台:2号窗口没有获取票源!!! 售货柜台:3号窗口没有获取票源!!! Process finished with exit code 0 //当买票时间大约等待时间时,则没有获取票源的窗口不买票,进入下个买票机会Shorten the ticket purchase time:
try { Thread.sleep(500);//人为加入买票时间 } catch (InterruptedException e) { e.printStackTrace(); }Execution result:
售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:9 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:8 售货柜台:3号窗口没有获取票源!!! 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:7 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:6 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:5 售货柜台:3号窗口没有获取票源!!! 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:4 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:3 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:2 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:1 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:0 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口买票时没票了000 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口买票时没票了000 Process finished with exit code 0 //等待时间内获取到票源了,也就卖出票了lockInterruptiblylockInterruptibly() When acquiring a lock through this method, if the lock is being held by another thread, it will enter the waiting state, but this waiting process can be interrupted by calling Thread The object's interrupt method can interrupt waiting. When interrupted, an InterruptedException is thrown, which needs to be caught or declared to be thrown.
public class ThreadTest { public static void main(String[] args) { SellRunnable sellRunnable = new SellRunnable(); Thread thread1 = new Thread(sellRunnable, "1号窗口"); Thread thread2 = new Thread(sellRunnable, "2号窗口"); Thread thread3 = new Thread(sellRunnable, "3号窗口"); thread1.start(); try { Thread.sleep(500);//确保窗口1号先获取锁 } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); thread3.start(); try { Thread.sleep(2000);//等待两秒后,打断窗口2、3的等待 } catch (InterruptedException e) { e.printStackTrace(); } thread2.interrupt(); thread3.interrupt(); } } SellRunnable中等待时间加长: try { Thread.sleep(5000);//人为加入买票时间 } catch (InterruptedException e) { e.printStackTrace(); }Execution results:
售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:9 售货柜台:3号窗口被打断了 //这个地方被打断了 售货柜台:2号窗口被打断了 //这个地方被打断了 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:8 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:7 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:6 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:5 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:4 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:3 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口卖出了一张票,剩余:2 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口卖出了一张票,剩余:1 售货柜台:1号窗口获取了票源+++++ 售货柜台:1号窗口卖出了一张票,剩余:0 售货柜台:2号窗口获取了票源+++++ 售货柜台:2号窗口买票时没票了000 售货柜台:3号窗口获取了票源+++++ 售货柜台:3号窗口买票时没票了000 Process finished with exit code 0
Comparison between synchronized and Lock
Through the above code, you can see the difference between Lock and synchronized Several connections and differences: Both are reentrant locksReentrant lock means that after a thread acquires the object lock, the thread can acquire the object lock again without Not blocked. For example, after multiple methods (or a method called recursively) in the same class are synchronized or locked, the same thread can acquire the object's lock without being blocked when calling these two methods. Example of non-reentrant lock:public class Lock{ private boolean isLocked = false; public void lock(){ while(isLocked){ wait(); } isLocked = true; } public void unlock(){ isLocked = false; notify(); } } //使用方法: public class Test{ Lock lock = new Lock(); public void test1(){ lock.lock(); test2(); lock.unlock(); } public void test2(){ lock.lock(); ... lock.unlock(); } }When the Test class calls the test1 method and calls test2 after executing lock.lock(), it will wait forever and become dead. Lock. Reentrant lock design principle:
public class Lock{ private boolean isLocked = false; private Thread lockedThread = null; int lockedCount = 0; public void lock(){ Thread thread = Thread.currentThread(); while(isLocked && thread != lockedThread){ wait(); } isLocked = true; lockedCount++; lockedThread = thread; } public void unlock(){ Thread thread = Thread.currentThread(); if(thread == lockedThread){ lockedCount--; if(lockedCount == 0){ isLocked = false; lockedThread = null; notify(); } } } }After calling the test1 method of the Test class, the test2 method can also be executed smoothly. In terms of implementation, synchronized basically uses counters to achieve reentrancy. Lock is an interruptible lock, synchronized cannot be interrupted.
当一个线程B执行被锁的对象的代码时,发现线程A已经持有该锁,那么线程B就会进入等待,但是synchronized就无法中断该等待过程,而Lock就可以通过lockInterruptibly方法抛出异常从而中断等待,去处理别的事情。
Lock可创建公平锁,synchronized是非公平锁。
公平锁的意思是按照请求的顺序来获取锁,不平公锁就无法保证线程获取锁的先后次序。
Lock可以知道是否获取到锁,synchronized不可以。
synchronized在发生异常或者运行完毕,会自动释放线程占有的锁。而Lock需要主动释放锁,否则会锁死;
synchronized在阻塞时,别的线程无法获取锁,Lock可以(这也是lock设计的一个目的)。
读写锁
多个线程对同一个文件进行写操作时,会发生冲突所以需要加锁,但是对同一个文件进行读操作的时候,使用上面的方法会造成效率的降低,所以基于这种情况,产生了ReadWriteLock这个接口:
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading. */ Lock readLock();//读的锁 /** * Returns the lock used for writing. * * @return the lock used for writing. */ Lock writeLock();//写的锁 }
这个接口的实现类是ReentrantReadWriteLock,其源代码如下:
public class ReentrantReadWriteLock implements ReadWriteLock, Serializable { private static final long serialVersionUID = -6992448646407690164L; private final ReentrantReadWriteLock.ReadLock readerLock; private final ReentrantReadWriteLock.WriteLock writerLock; ... public ReentrantReadWriteLock.WriteLock writeLock() {//获取write lock return this.writerLock; } public ReentrantReadWriteLock.ReadLock readLock() {//获取read lock return this.readerLock; } ... }
使用方法和Lock一样,使用到write时调用writeLock()方法获取lock进行加锁,使用到read时调用readLock()方法进行加锁,需要注意的知识点如下:
线程A占用写锁,线程B在申请写、读的时候需要等待。
线程A占用读锁,线程B在申请写操作时,需要等待。
线程A占用读锁,线程B获取读操作时可以获取到。
总结
如果需要效率提升,则建议使用Lock,如果效率要求不高,则synchronized满足使用条件,业务逻辑写起来也简单,不需要手动释放锁。
PHP中文网,有大量免费的JAVA入门教程,欢迎大家学习!
The above is the detailed content of what is java thread synchronization. For more information, please follow other related articles on the PHP Chinese website!