|
3 | 3 | * [进程与线程](#进程与线程) |
4 | 4 | * [单线程与多线程](#单线程与多线程) |
5 | 5 | * [实现线程的4中方式](#实现线程的4中方式) |
| 6 | + * [thread.start()和runnable.run()的区别](#thread.start()和runnable.run()的区别) |
| 7 | + * [Thread和Runnable的异同](#Thread和Runnable的异同) |
| 8 | + * [线程的基本操作](#线程的基本操作) |
| 9 | + * [线程的优先级与守护线程](#线程的优先级与守护线程) |
| 10 | + * [线程的状态与转换](#线程的状态与转换) |
| 11 | + * [synchronized关键字](#synchronized关键字) |
| 12 | + * [实例锁与全局锁](#实例锁与全局锁) |
| 13 | + * [wait和notify](#wait和notify) |
6 | 14 |
|
7 | 15 | ## 基础概念 |
8 | 16 |
|
@@ -212,6 +220,269 @@ public void foo() { |
212 | 220 | 2. 当一个线程访问一个对象的synchronized方法或者synchronized代码块时,其他线程对该对象的非synchronized方法的访问将不会被阻塞。 |
213 | 221 | 3. 当一个线程访问一个对象的synchronized方法或者synchronized代码块时,其他线程对该对象的其他synchronized方法或代码块的访问将会被阻塞。 |
214 | 222 |
|
| 223 | +### 实例锁与全局锁 |
| 224 | +> 实例锁:锁在某一个实例对象上。如果该类是单例,那么该锁也具有全局锁的概念。实例锁对应的就是synchronized关键字。 |
| 225 | +```java |
| 226 | +synchronized(this) synchronized(obj) |
| 227 | +public synchronized void foo() |
| 228 | +``` |
| 229 | +> 全局锁:该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。 |
| 230 | +```java |
| 231 | +synchronized(XXXClass.class) |
| 232 | +public static synchronized void foo() |
| 233 | +``` |
| 234 | +> 例子 |
| 235 | +```java |
| 236 | +pulbic class Something { |
| 237 | + public synchronized void isSyncA(){} |
| 238 | + public synchronized void isSyncB(){} |
| 239 | + public static synchronized void cSyncA(){} |
| 240 | + public static synchronized void cSyncB(){} |
| 241 | +} |
| 242 | +``` |
| 243 | +假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况。 |
| 244 | +1. x.isSyncA()与x.isSyncB() 不能同时访问。实例锁,访问两个同步方法的对象是同一个对象x |
| 245 | +2. x.isSyncA()与y.isSyncA() 能同时访问。实例锁,访问同一个同步方法的对象是两个不同的对象,实例锁不是同一个 |
| 246 | +3. x.cSyncA()与y.cSyncB() 不能同时访问。因为cSyncA()和cSyncB()都是static类型,x.cSyncA()相当于Something.isSyncA(),y.cSyncB()相当于Something.isSyncB(),因此它们共用一个同步锁,不能被同时反问。 |
| 247 | +4. x.isSyncA()与Something.cSyncA() 可以被同时访问。因为isSyncA()是实例方法,x.isSyncA()使用的是对象x的锁;而cSyncA()是静态方法,Something.cSyncA()可以理解对使用的是“类的锁”。因此,它们是可以被同时访问的。 |
| 248 | +```java |
| 249 | +public class SynchronizedLockExample { |
| 250 | + |
| 251 | + public static void main(String[] args) { |
| 252 | + SynchronizedLock x = new SynchronizedLock(); |
| 253 | + // x.syncA()与x.syncB() |
| 254 | + new Thread(()-> { |
| 255 | + try { |
| 256 | + x.syncA(); |
| 257 | + } catch (InterruptedException e) { |
| 258 | + e.printStackTrace(); |
| 259 | + } |
| 260 | + }, "Threadx ").start(); |
| 261 | + new Thread(()-> { |
| 262 | + try { |
| 263 | + x.syncB(); |
| 264 | + } catch (InterruptedException e) { |
| 265 | + e.printStackTrace(); |
| 266 | + } |
| 267 | + }, "Thready ").start(); |
| 268 | + /** 实例锁。不能同时访问 |
| 269 | + * Threadx 0 |
| 270 | + * Threadx 1 |
| 271 | + * Threadx 2 |
| 272 | + * Thready 0 |
| 273 | + * Thready 1 |
| 274 | + * Thready 2 |
| 275 | + */ |
| 276 | + // x.syncA()与y.syncA() |
| 277 | + SynchronizedLock y = new SynchronizedLock(); |
| 278 | + SynchronizedLock y2 = new SynchronizedLock(); |
| 279 | + new Thread(() -> { |
| 280 | + try { |
| 281 | + y.syncA(); |
| 282 | + } catch (InterruptedException e) { |
| 283 | + e.printStackTrace(); |
| 284 | + } |
| 285 | + }, "Thready1").start(); |
| 286 | + new Thread(() -> { |
| 287 | + try { |
| 288 | + y2.syncA(); |
| 289 | + } catch (InterruptedException e) { |
| 290 | + e.printStackTrace(); |
| 291 | + } |
| 292 | + }, "Thready2").start(); |
| 293 | + /**实例锁。可以同时访问,实例不是同一个对象锁 |
| 294 | + * Thready10 |
| 295 | + * Thready20 |
| 296 | + * Thready21 |
| 297 | + * Thready11 |
| 298 | + * Thready22 |
| 299 | + * Thready12 |
| 300 | + */ |
| 301 | + // x.syncC()与y.syncD() |
| 302 | + SynchronizedLock x1 = new SynchronizedLock(); |
| 303 | + SynchronizedLock y3 = new SynchronizedLock(); |
| 304 | + new Thread(()-> { |
| 305 | + try { |
| 306 | + x1.syncC(); |
| 307 | + } catch (InterruptedException e) { |
| 308 | + e.printStackTrace(); |
| 309 | + } |
| 310 | + }, "Threadx1 ").start(); |
| 311 | + new Thread(()-> { |
| 312 | + try { |
| 313 | + y3.syncD(); |
| 314 | + } catch (InterruptedException e) { |
| 315 | + e.printStackTrace(); |
| 316 | + } |
| 317 | + }, "Thready3 ").start(); |
| 318 | + /** 全局锁。不能同时访问,static synchronized修饰的方法是全局静态的,与实例无关 |
| 319 | + * Threadx1 0 |
| 320 | + * Threadx1 1 |
| 321 | + * Threadx1 2 |
| 322 | + * Thready3 0 |
| 323 | + * Thready3 1 |
| 324 | + * Thready3 2 |
| 325 | + */ |
| 326 | + // x.syncA与SynchronizedLock.syncD |
| 327 | + SynchronizedLock x3 = new SynchronizedLock(); |
| 328 | + new Thread(()-> { |
| 329 | + try { |
| 330 | + x3.syncA(); |
| 331 | + } catch (InterruptedException e) { |
| 332 | + e.printStackTrace(); |
| 333 | + } |
| 334 | + }, "Theradx3").start(); |
| 335 | + new Thread(() -> { |
| 336 | + try { |
| 337 | + SynchronizedLock.syncD(); |
| 338 | + } catch (InterruptedException e) { |
| 339 | + e.printStackTrace(); |
| 340 | + } |
| 341 | + }, "Threadstatic ").start(); |
| 342 | + /** 可以同时访问。x.syncA是实例锁,SynchronizedLock.syncD是全局锁 |
| 343 | + * Theradx30 |
| 344 | + * Threadstatic 0 |
| 345 | + * Theradx31 |
| 346 | + * Threadstatic 1 |
| 347 | + * Theradx32 |
| 348 | + * Threadstatic 2 |
| 349 | + */ |
| 350 | + } |
| 351 | +} |
| 352 | + |
| 353 | +class SynchronizedLock { |
| 354 | + |
| 355 | + public synchronized void syncA() throws InterruptedException { |
| 356 | + for (int i = 0; i < 3; i++) { |
| 357 | + System.out.println(Thread.currentThread().getName() + i); |
| 358 | + Thread.sleep(1000); |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + public synchronized void syncB() throws InterruptedException { |
| 363 | + for (int i = 0; i < 3; i++) { |
| 364 | + System.out.println(Thread.currentThread().getName() + i); |
| 365 | + Thread.sleep(1000); |
| 366 | + } |
| 367 | + } |
| 368 | + |
| 369 | + public static synchronized void syncC() throws InterruptedException { |
| 370 | + for (int i = 0; i < 3; i++) { |
| 371 | + System.out.println(Thread.currentThread().getName() + i); |
| 372 | + Thread.sleep(1000); |
| 373 | + } |
| 374 | + } |
| 375 | + |
| 376 | + public static synchronized void syncD() throws InterruptedException { |
| 377 | + for (int i = 0; i < 3; i++) { |
| 378 | + System.out.println(Thread.currentThread().getName() + i); |
| 379 | + Thread.sleep(1000); |
| 380 | + } |
| 381 | + } |
| 382 | +} |
| 383 | +``` |
| 384 | + |
| 385 | +### wait和notify |
| 386 | +> wait, notify, notifyAll |
| 387 | +在Object.java中,定义了wait(), notify()和notifyAll()等接口。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。 |
| 388 | + |
| 389 | +Object类中关于等待/唤醒的API详细信息如下: |
| 390 | +* notify() -- 唤醒在此对象监视器上等待的单个线程。 |
| 391 | +* notifyAll() -- 唤醒在此对象监视器上等待的所有线程。 |
| 392 | +* wait() -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。 |
| 393 | +* wait(long timeout) -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。 |
| 394 | +* wait(long timeout, int nanos) -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。 |
| 395 | +```java |
| 396 | +public class NotifyExample { |
| 397 | + |
| 398 | + public static void main(String[] args) { |
| 399 | + Notify notify = new Notify(); |
| 400 | + new Thread(()-> { |
| 401 | + synchronized (notify) { |
| 402 | + while (notify.flag) { |
| 403 | + System.out.println("User A"); |
| 404 | + try { |
| 405 | + notify.wait(); |
| 406 | + } catch (InterruptedException e) { |
| 407 | + e.printStackTrace(); |
| 408 | + } |
| 409 | + } |
| 410 | + } |
| 411 | + notify.call(); |
| 412 | + synchronized (notify) { |
| 413 | + notify.notifyAll(); |
| 414 | + } |
| 415 | + }, "User A").start(); |
| 416 | + |
| 417 | + new Thread(()-> { |
| 418 | + synchronized (notify) { |
| 419 | + while (notify.flag) { |
| 420 | + System.out.println("User B"); |
| 421 | + try { |
| 422 | + notify.wait(); |
| 423 | + } catch (InterruptedException e) { |
| 424 | + e.printStackTrace(); |
| 425 | + } |
| 426 | + } |
| 427 | + } |
| 428 | + notify.call(); |
| 429 | + synchronized (notify) { |
| 430 | + notify.notifyAll(); |
| 431 | + } |
| 432 | + }, "User B").start(); |
| 433 | + /** |
| 434 | + * Begin to call |
| 435 | + * User A calling 0% |
| 436 | + * User B |
| 437 | + * User A calling 50% |
| 438 | + * User A calling 100% |
| 439 | + * End to call |
| 440 | + * Begin to call |
| 441 | + * User B calling 0% |
| 442 | + * User B calling 50% |
| 443 | + * User B calling 100% |
| 444 | + * End to call |
| 445 | + */ |
| 446 | + } |
| 447 | +} |
| 448 | + |
| 449 | +class Notify { |
| 450 | + public boolean flag = false; |
| 451 | + public void call() { |
| 452 | + flag = true; |
| 453 | + System.out.println("Begin to call"); |
| 454 | + for (int i = 0; i < 101; i+=50) { |
| 455 | + System.out.println(Thread.currentThread().getName() + " calling " + i + "%"); |
| 456 | + try { |
| 457 | + Thread.sleep(100); |
| 458 | + } catch (InterruptedException e) { |
| 459 | + e.printStackTrace(); |
| 460 | + } |
| 461 | + } |
| 462 | + System.out.println("End to call"); |
| 463 | + flag = false; |
| 464 | + } |
| 465 | +} |
| 466 | +``` |
| 467 | +> 注意事项 |
| 468 | +* “当前线程”在调用wait()时,必须拥有该对象的同步锁。该线程调用wait()之后,会释放该锁;然后一直等待直到“其它线程”调用对象的同步锁的notify()或notifyAll()方法。然后,该线程继续等待直到它重新获取“该对象的同步锁”,然后就可以接着运行。, synchronized(obj),否则会出现`java.lang.IllegalMonitorStateException` |
| 469 | +* 调用notify时也需要获得该对象的“同步锁”,jdk中的注释: |
| 470 | +```md |
| 471 | +This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways: |
| 472 | +1. By executing a synchronized instance method of that object. 通过获得该对象的同步锁 |
| 473 | +2. By executing the body of a {@code synchronized} statement that synchronizes on the object. 在该对象的同步代码块中执行 |
| 474 | +3. For objects of type {@code Class,} by executing a synchronized static method of that class. 通过执行全局锁的方法 |
| 475 | +``` |
| 476 | +* Only one thread at a time can own an object's monitor. |
| 477 | + |
| 478 | +> 为什么notify(), wait()等函数定义在Object中,而不是Thread中 |
| 479 | +Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。 |
| 480 | +wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行! |
| 481 | +OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”。 |
| 482 | +负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。 |
| 483 | + |
| 484 | +总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。 |
| 485 | + |
215 | 486 | ### 生产者消费者问题 |
216 | 487 |
|
217 | 488 | https://www.cnblogs.com/skywang12345/ |
|
0 commit comments