1.多线程的同步
为什么要引入同步机制
- 在多线程环境中,可能会有两个甚至更多的线程试图同时 访问一个有限的资源。必须对这种潜在资源冲突进行预防。
- 解决方法:在线程使用一个资源时为其加锁即可。访问资 源的第一个线程为其加上锁以后,其他线程便不能再使用 那个资源,除非被解锁。
问题代码举例
俩个线程同时取账户的钱
public class FetchMoney {
public static void main(String[] args) {
Bank bank = new Bank();
MoneyTherd moneyTherd = new MoneyTherd(bank);
moneyTherd.setName("银行柜台");
MoneyTherd moneyTherd2 = new MoneyTherd(bank);
moneyTherd2.setName("ATM");
moneyTherd.start();
moneyTherd2.start();
}
}
class Bank {
private int money = 1000;
public int getMoney(int number) {
if (number < 0) {
return -1;
} else if (number > money) {
return -2;
} else if (money < 0) {
return -3;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
money -= number;
System.out.println("剩余 money:" + money);
return number;
}
}
}
class MoneyTherd extends Thread {
private Bank bank;
public MoneyTherd(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println("取出:" + bank.getMoney(800));
}
}
class MoneyRunnableTherd implements Runnable {
private Bank bank;
public void setBank(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println("取出:" + bank.getMoney(800));
}
}
运行结果
2.synchronized修饰方法
- synchronized 关键字:当 synchronized 关键字修饰一个方 法的时候,该方法叫做同步方法。
- Java 中的每个对象都有一个锁(lock)或者叫做监视器 (monitor),当访问某个对象的 synchronized 方法时,表 示将该对象上锁,此时其他任何线程都无法再去访问该 synchronized 方法了,直到之前的那个线程执行方法完毕 后(或者是抛出了异常),那么将该对象的锁释放掉,其 他线程才有可能再去访问该synchronized 方法。
- 如果一个对象有多个 synchronized 方法,某一时刻某个 线程已经进入到了某个 synchronized 方法,那么在该方法 没有执行完毕前,其他线程是无法访问该对象的任何 synchronized 方法的。
举例一
public class FetchMoney {
public static void main(String[] args) {
Bank bank = new Bank();
MoneyTherd moneyTherd = new MoneyTherd(bank);
moneyTherd.setName("银行柜台");
MoneyTherd moneyTherd2 = new MoneyTherd(bank);
moneyTherd2.setName("ATM");
moneyTherd.start();
moneyTherd2.start();
}
}
class Bank {
private int money = 1000;
public synchronized int getMoney(int number) {
if (number < 0) {
return -1;
} else if (number > money) {
return -2;
} else if (money < 0) {
return -3;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
money -= number;
System.out.println("剩余 money:" + money);
return number;
}
}
}
class MoneyTherd extends Thread {
private Bank bank;
public MoneyTherd(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println("取出:" + bank.getMoney(800));
}
}
运行结果: 举例二 如果一个对象有多个 synchronized 方法,某一时刻某个 线程已经进入到了某个 synchronized 方法,那么在该方法 没有执行完毕前,其他线程是无法访问该对象的任何 synchronized 方法的。
public class ThredTeat1 {
public static void main(String[] args) {
Example example = new Example();
TheThred theThred = new TheThred(example);
TheThred2 theThred2 = new TheThred2(example);
theThred.start();
theThred2.start();
}
}
class Example {
public synchronized void execute () {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("hello:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void execute2 () {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("word:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TheThred extends Thread {
private Example example;
public TheThred(Example example) {
this.example = example;
}
@Override
public void run() {
example.execute();
}
}
class TheThred2 extends Thread {
private Example example;
public TheThred2(Example example) {
this.example = example;
}
@Override
public void run() {
example.execute2();
}
}
运行结果:
hello:0
hello:1
hello:2
hello:3
hello:4
word:0
word:1
word:2
word:3
word:4
Process finished with exit code 0
3.synchronized修饰static方法
如果某个 synchronized方法是 static的, ,那么当线程访问该方法时, 它锁的并不是synchronized方法所在的对象,而是synchronized 方 法所在的对象所对应的Class对象,因为Java中无论一个类有多少 个对象,这些对象会对应唯一一个Class对象,因此当线程分别访 问同一个类的两个对象的两个 static, synchronized 方法时,他们 的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行 完毕后另一个线程才开始执行。
static的方法属于类方法,它属于这个Class(注意:这里的Class不是指Class的某个具体对象),那么static获取到的锁,就是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。而‘非static’方法获取到的锁,就是当前调用这个方法的对象的锁了。所以,他们之间不会产生互斥。
public class ThredTeat1 {
public static void main(String[] args) {
Example example = new Example();
TheThred theThred = new TheThred(example);
TheThred2 theThred2 = new TheThred2(example);
theThred.start();
theThred2.start();
}
}
class Example {
public synchronized void execute () {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("hello:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void execute2 () {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("word:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TheThred extends Thread {
private Example example;
public TheThred(Example example) {
this.example = example;
}
@Override
public void run() {
example.execute();
}
}
class TheThred2 extends Thread {
private Example example;
public TheThred2(Example example) {
this.example = example;
}
@Override
public void run() {
example.execute2();
}
}
运行结果: execute() 和 execute2() 同时执行,说明锁的不是同一个对象
hello:0
word:0
word:1
hello:1
word:2
hello:2
word:3
hello:3
word:4
hello:4
Process finished with exit code 0
举例二
public class ThredTeat1 {
public static void main(String[] args) {
Example example = new Example();
TheThred theThred = new TheThred(example);
example = new Example();
TheThred2 theThred2 = new TheThred2(example);
theThred.start();
theThred2.start();
}
}
class Example {
public synchronized static void execute () {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("hello:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void execute2 () {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("word:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TheThred extends Thread {
private Example example;
public TheThred(Example example) {
this.example = example;
}
@Override
public void run() {
example.execute();
}
}
class TheThred2 extends Thread {
private Example example;
public TheThred2(Example example) {
this.example = example;
}
@Override
public void run() {
example.execute2();
}
}
运行结果
hello:0
hello:1
hello:2
hello:3
hello:4
word:0
word:1
word:2
word:3
word:4
Process finished with exit code 0
4.synchronized代码块
synchronized代码块写法
synchronized(object) {
} 表示线程执行时会对object上锁
public class TherdTest2 {
public static void main(String[] args) {
Example2 example2 = new Example2();
ThredTest thredTest = new ThredTest(example2);
ThredTest1 thredTest1 = new ThredTest1(example2);
thredTest.start();
thredTest1.start();
}
}
class Example2 {
private Object object = new Object();
public void execute() {
synchronized (object) {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("hello:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void execute2() {
synchronized (object) {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(500);
System.out.println("word:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThredTest extends Thread {
private Example2 example;
public ThredTest(Example2 example) {
this.example = example;
}
@Override
public void run() {
example.execute();
}
}
class ThredTest1 extends Thread {
private Example2 example;
public ThredTest1(Example2 example) {
this.example = example;
}
@Override
public void run() {
example.execute2();
}
}
运行结果
hello:0
hello:1
hello:2
hello:3
hello:4
word:0
word:1
word:2
word:3
word:4
Process finished with exit code 0
5.小结
synchronized 方法是一种粗粒度的并发控制,某一时刻, 只能有一个线程执行该synchronized 方法; synchronized 块 则是一种细粒度的并发控制,只会将块中的代码同步,位 于方法内、synchronized 块之外的代码是可以被多个线程 同时访问到的。
6.使用wait及notify方法实现线程间的通信
wait 与 notify 方法都是定义在 Object 类中,而且是 final 的,因此会被所有的 Java类所继承并且无法重写。这两个 方法要求在调用时线程应该已经获得了对象的锁,因此对 这两个方法的调用需要放在synchronized方法或块当中。
当线程执行了 wait 方法时,它会释放掉对象的锁。
另一个会导致线程暂停的方法就是 Thread 类的 sleep方法, 它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中 是不会释放掉对象的锁的。
代码举例
public class Sample {
private int number;
public synchronized void increase() {
while (number != 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(number);
notify();
}
public synchronized void decrease() {
while (number == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
}
public class IncreaseThred extends Thread{
private Sample sample;
public IncreaseThred(Sample sample) {
this.sample = sample;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(600);
sample.increase();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class DecreaseThred extends Thread{
private Sample sample;
public DecreaseThred(Sample sample) {
this.sample = sample;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(500);
sample.decrease();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Maintest {
public static void main(String[] args) {
Sample sample = new Sample();
IncreaseThred increaseThred = new IncreaseThred(sample);
DecreaseThred decreaseThred = new DecreaseThred(sample);
IncreaseThred increaseThred1 = new IncreaseThred(sample);
DecreaseThred decreaseThred1 = new DecreaseThred(sample);
increaseThred.start();
decreaseThred.start();
increaseThred1.start();
decreaseThred1.start();
}
}
运行结果
1
0
1
0
1
0
1
0
1
0
1
0
1
Process finished with exit code 0
线程状态图
|