一.问题引出
TicketWindowRunnable.java
package com.miracle.concurrency.chapter7;
public class TicketWindowRunnable implements Runnable{
private int index = 1;
private final static int MAX = 50;
@Override
public void run() {
while (true) {
if (index > MAX){
break;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + " 的号码是:" + (index++));
}
}
}
BankVersion.java
package com.miracle.concurrency.chapter7;
public class BankVersion {
public static void main(String[] args) {
final TicketWindowRunnable ticketWindow = new TicketWindowRunnable();
Thread windowThread1 = new Thread(ticketWindow, "一号窗口");
Thread windowThread2 = new Thread(ticketWindow, "二号窗口");
Thread windowThread3 = new Thread(ticketWindow, "三号窗口");
windowThread1.start();
windowThread2.start();
windowThread3.start();
}
}
执行BankVersion
发现超过50了,由此引出多线程安全问题。此问题一般发生于多个线程操作同一数据
二.synchronized关键字
1.理解
关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。
2.synchronized的三种应用方式
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
- 1)普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
- 2)静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
- 3)同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
3.synchronized代码示例
- 上面线程安全问题可以通过下面代码解决
package com.miracle.concurrency.chapter7;
public class TicketWindowRunnable implements Runnable{
private int index = 1;
private final static int MAX = 50;
private final Object MONITOB = new Object();
@Override
public void run() {
while (true) {
synchronized (MONITOB){
if (index > MAX){
break;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + " 的号码是:" + (index++));
}
}
}
}
4.synchronized this锁
synchronized 加在代码块上时,需要指定锁对象,当synchronized加在方法上时,不需要(这是使用的是this锁)
SynchronizedThis.java
package com.miracle.concurrency.chapter7;
public class SynchronizedThis {
public static void main(String[] args) {
ThisLock thisLock = new ThisLock();
/*
这里开启两个线程,分别执行ThisLock.m1,
ThisLock.m2 方法
由于 synchronized 加在方法上时,默认使用this对象当锁
导致两个线程用一个锁(虽然执行的不是同一个方法)
执行结果为两个线程顺序执行
*/
new Thread("T1"){
@Override
public void run() {
thisLock.m1();
}
}.start();
new Thread("T2"){
@Override
public void run() {
thisLock.m2();
}
}.start();
}
}
class ThisLock{
public synchronized void m1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void m2(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
接下来改写成代码块锁的形式
SynchronizedThis.java
package com.miracle.concurrency.chapter7;
public class SynchronizedThis {
public static void main(String[] args) {
ThisLock thisLock = new ThisLock();
new Thread("T1"){
@Override
public void run() {
thisLock.m1();
}
}.start();
new Thread("T2"){
@Override
public void run() {
thisLock.m2();
}
}.start();
}
}
class ThisLock{
private final Object LOCK = new Object();
public synchronized void m1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void m2(){
synchronized(LOCK){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5.synchronized class锁
当synchronized加到静态方法上时,用的是class锁
SynchronizedStatic.java
package com.miracle.concurrency.chapter7;
public class SynchronizedStatic {
static {
synchronized (SynchronizedStatic.class) {
System.out.println("static " + Thread.currentThread().getName());
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized static void m1(){
System.out.println("m1 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void m2(){
System.out.println("m2 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void m3(){
System.out.println("m3 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类
SynchronizedStaticTest.java
package com.miracle.concurrency.chapter7;
public class SynchronizedStaticTest {
public static void main(String[] args) {
new Thread("T1"){
@Override
public void run() {
SynchronizedStatic.m1();
}
}.start();
new Thread("T2"){
@Override
public void run() {
SynchronizedStatic.m2();
}
}.start();
new Thread("T3"){
@Override
public void run() {
SynchronizedStatic.m3();
}
}.start();
}
}
三.死锁问题
1.死锁示例
DeadLock.java
package com.miracle.concurrency.chapter8;
public class DeadLock {
private OtherService otherService;
public DeadLock(OtherService otherService) {
this.otherService = otherService;
}
private final Object LOCK = new Object();
public void m1(){
synchronized (LOCK){
System.out.println("m1");
otherService.s1();
}
}
public void m2(){
synchronized (LOCK){
System.out.println("m2");
}
}
}
OtherService.java
package com.miracle.concurrency.chapter8;
public class OtherService {
private final Object LOCK = new Object();
private DeadLock deadLock;
public void s1() {
synchronized (LOCK) {
System.out.println("s1=============");
}
}
public void s2(){
synchronized (LOCK) {
System.out.println("s2=============");
deadLock.m2();
}
}
public void setDeadLock(DeadLock deadLock) {
this.deadLock = deadLock;
}
}
测试程序
DeadLockTest.java
package com.miracle.concurrency.chapter8;
public class DeadLockTest {
public static void main(String[] args) {
OtherService otherService = new OtherService();
DeadLock deadLock = new DeadLock(otherService);
otherService.setDeadLock(deadLock);
new Thread(){
@Override
public void run() {
while (true){
deadLock.m1();
}
}
}.start();
new Thread(){
@Override
public void run() {
while (true) {
otherService.s2();
}
}
}.start();
}
}
2.查看程序中的死锁
在终端命令行上执行:jps找到程序的java进程号
执行:jstack + 刚才的进程号
根据这里发现两个线程产生死锁