一、同步代码块
用synchronized把要出现线程安全问题的代码包起来,每次只能有一个线程执行
public class TestWindow1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window1 implements Runnable {
int ticket = 100; // 共享数据
public void run() {
while (true) {
synchronized (this) {
if (ticket > 0) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "售票,票号为:" + ticket--);
} else {
break;
}
}
}
}
}
上面代码中synchronized使用的锁为当前对象,即创建的Window1的实例,当然也可以使用其他的类实例
public class TestWindow2 {
public static void main(String[] args) {
Window2 w1 = new Window2();
Window2 w2 = new Window2();
Window2 w3 = new Window2();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
class Window2 extends Thread {
static int ticket = 100;
static Object obj = new Object();
public void run() {
while (true) {
// synchronized (this) {//在本问题中,this表示:w1,w2,w3
synchronized (obj) {
if (ticket > 0) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "售票,票号为:" + ticket--);
} else {
break;
}
}
}
}
}
上面的代码中,创建多线程的方式与第一种不同,这里是继承Thread 类的方式,因此在创建三个线程的时候,创建了三个Window2的实例对象,所以再使用this作为锁的时候,this为三个对象本身,所以三个线程使用的并不是同一把锁,所以并不能达到线程同步的目的,这里要使用其他的类对象来充当锁
二、同步方法
public class SynchronizedTest1 {
public synchronized void methodA() {
try {
for (int i = 0; i < 3; i++) {
System.out.println("methodA-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB() {
synchronized (this) {
try {
for (int i = 0; i < 3; i++) {
System.out.println("methodB-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void methodC() {
Object obj = new Object();
synchronized (obj) {
try {
for (int i = 0; i < 3; i++) {
System.out.println("methodC-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedTest1 test1 = new SynchronizedTest1();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
test1.methodA();
}
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
test1.methodB();
}
});
thread2.start();
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
test1.methodC();
}
});
thread3.start();
}
}
执行结果:
methodA-0
methodC-0
methodA-1
methodC-1
methodA-2
methodC-2
methodB-0
methodB-1
methodB-2
在非静态方法上加synchronized修饰,同样能达到同步的目的,thread1 和 thread2 两个线程同时执行同一个对象被synchronized修饰的方法时,只能有一个线程执行,另一个线程处于等待,此时锁为this,即当前对象。基于上面的分析,下面的使用方式将不会达到同步的效果
class Window2 extends Thread {
static int ticket = 100;//共享数据
public void run() {
while (true) {
show();
}
}
public synchronized void show() {//this锁,这里创建了三个window的对象,执行的时候三个线程三把锁,所以同步方法不用在继承的方式中
if (ticket > 0) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售票,票号为:"
+ ticket--);
}
}
}
public class TestWindow2 {
public static void main(String[] args) {
Window2 w1 = new Window2();
Window2 w2 = new Window2();
Window2 w3 = new Window2();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
三、静态同步方法
public class SynchronizedTest2 {
public synchronized static void methodA() {
try {
for (int i = 0; i < 3; i++) {
System.out.println("methodA-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB() {
synchronized (SynchronizedTest2.class) {
try {
for (int i = 0; i < 3; i++) {
System.out.println("methodB-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedTest2 test2 = new SynchronizedTest2();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
test2.methodA();
}
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
test2.methodB();
}
});
thread2.start();
}
}
执行结果:
methodA-0
methodA-1
methodA-2
methodB-0
methodB-1
methodB-2
使用synchronized 修饰静态方法时的锁为当前类对象(注意不是当前类实例对象)即:
Class<SynchronizedTest2> aClass = SynchronizedTest2.class;
public class SynchronizedTest3 {
public synchronized void methodA() {
try {
for (int i = 0; i < 3; i++) {
System.out.println("methodA-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void methodB() {
try {
for (int i = 0; i < 3; i++) {
System.out.println("methodB-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedTest3 test3 = new SynchronizedTest3();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
test3.methodA();
}
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
test3.methodB();
}
});
thread2.start();
}
}
执行结果:
methodA-0
methodB-0
methodA-1
methodB-1
methodA-2
methodB-2
上面代码中,一个方法使用的是实例对象锁,一个方法使用的是类对象锁,两个线程使用两个把锁,可以同时执行。而当两个线程执行同一个类的两个实例对象的非静态方法时,也是同时执行的,原因是锁为两个实例对象,当执行的是静态方法时,两个线程则不能同时执行,因为锁还是同一把锁(类对象)
synchronized可重入锁?
public class Synchronized1 {
public synchronized void method1(){
System.out.println("method1方法执行");
method2();
}
public synchronized void method2(){
System.out.println("method2方法执行");
method3();
}
public synchronized void method3(){
System.out.println("method3方法执行");
}
public static void main(String[] args) {
Synchronized1 sd1 = new Synchronized1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd1.method1();
}
});
t1.start();
}
}
执行结果:
method1方法执行
method2方法执行
method3方法执行
上面代码可以看出synchronized 是可重入的,关于重入锁后面单独总结