一、 synchronized
多个线程如果共享资源的情况下, 很容易的就会导致资源出现混乱,就以买票的程序为例子,先创建一个SellTicket对象,设置总票数为100张的情况下,设置四个窗口售卖这一百张票。
// SellTicket.java
import com.sun.org.apache.xml.internal.resolver.Catalog;
public class SellTicket implements Runnable {
private int tickteCount = 100;
@Override
public void run() {
try {
while (tickteCount > 0) {
sellTicket();
// 让线程睡一会,以免太快把票卖完
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void sellTicket() {
if (tickteCount > 0) {
tickteCount--;
System.out.println(Thread.currentThread().getName() + "买票, " + "还剩" + tickteCount + "张票。");
} else {
System.out.println("票卖完了");
}
}
}
//Demo1.java
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
// 让多个线程操作一个对象的资源,而不是new多个SellTicket() 对象.
SellTicket ticket = new SellTicket();
Thread t1 = new Thread(ticket,"窗口1");
Thread t2 = new Thread(ticket,"窗口2");
Thread t3 = new Thread(ticket,"窗口3");
Thread t4 = new Thread(ticket,"窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
`
程序运行之后,出现如下结果:
窗口1买票, 还剩98张票。
窗口2买票, 还剩98张票。
窗口3买票, 还剩97张票。
窗口4买票, 还剩96张票。
窗口1买票, 还剩94张票。
窗口3买票, 还剩93张票。
窗口2买票, 还剩94张票。
窗口4买票, 还剩92张票。
窗口1买票, 还剩90张票。
窗口4买票, 还剩89张票。
窗口2买票, 还剩90张票。
窗口3买票, 还剩90张票。
窗口1买票, 还剩87张票。
窗口3买票, 还剩86张票。
窗口4买票, 还剩87张票。
窗口2买票, 还剩87张票。
窗口1买票, 还剩85张票。
窗口2买票, 还剩82张票。
窗口4买票, 还剩83张票。
窗口3买票, 还剩83张票。
窗口4买票, 还剩81张票。
窗口3买票, 还剩80张票。
窗口2买票, 还剩81张票。
窗口1买票, 还剩81张票。
窗口2买票, 还剩78张票。
窗口1买票, 还剩77张票。
窗口3买票, 还剩78张票。
窗口4买票, 还剩78张票。
窗口2买票, 还剩75张票。
窗口4买票, 还剩73张票。
窗口3买票, 还剩74张票。
窗口1买票, 还剩75张票。
窗口2买票, 还剩72张票。
窗口1买票, 还剩69张票。
窗口3买票, 还剩70张票。
窗口4买票, 还剩71张票。
窗口2买票, 还剩67张票。
窗口4买票, 还剩65张票。
窗口3买票, 还剩66张票。
窗口1买票, 还剩67张票。
窗口4买票, 还剩64张票。
窗口2买票, 还剩64张票。
窗口3买票, 还剩62张票。
窗口1买票, 还剩62张票。
窗口4买票, 还剩60张票。
窗口1买票, 还剩59张票。
窗口2买票, 还剩60张票。
窗口3买票, 还剩60张票。
窗口4买票, 还剩58张票。
窗口3买票, 还剩55张票。
窗口1买票, 还剩56张票。
窗口2买票, 还剩56张票。
窗口4买票, 还剩54张票。
窗口3买票, 还剩53张票。
窗口2买票, 还剩51张票。
窗口1买票, 还剩52张票。
窗口4买票, 还剩50张票。
窗口2买票, 还剩49张票。
窗口1买票, 还剩48张票。
窗口3买票, 还剩48张票。
窗口4买票, 还剩47张票。
窗口2买票, 还剩46张票。
窗口1买票, 还剩45张票。
窗口3买票, 还剩44张票。
窗口4买票, 还剩43张票。
窗口2买票, 还剩42张票。
窗口3买票, 还剩40张票。
窗口1买票, 还剩41张票。
窗口4买票, 还剩39张票。
窗口3买票, 还剩37张票。
窗口1买票, 还剩36张票。
窗口2买票, 还剩37张票。
窗口4买票, 还剩35张票。
窗口2买票, 还剩33张票。
窗口1买票, 还剩32张票。
窗口3买票, 还剩32张票。
窗口4买票, 还剩31张票。
窗口1买票, 还剩30张票。
窗口2买票, 还剩29张票。
窗口3买票, 还剩30张票。
窗口4买票, 还剩28张票。
窗口1买票, 还剩27张票。
窗口3买票, 还剩25张票。
窗口2买票, 还剩26张票。
窗口4买票, 还剩24张票。
窗口1买票, 还剩23张票。
窗口2买票, 还剩21张票。
窗口3买票, 还剩22张票。
窗口4买票, 还剩20张票。
窗口1买票, 还剩19张票。
窗口3买票, 还剩18张票。
窗口2买票, 还剩19张票。
窗口4买票, 还剩17张票。
窗口1买票, 还剩16张票。
窗口3买票, 还剩16张票。
窗口2买票, 还剩15张票。
窗口4买票, 还剩14张票。
窗口1买票, 还剩13张票。
窗口2买票, 还剩12张票。
窗口3买票, 还剩12张票。
窗口4买票, 还剩11张票。
窗口1买票, 还剩10张票。
窗口3买票, 还剩8张票。
窗口2买票, 还剩9张票。
窗口4买票, 还剩7张票。
窗口3买票, 还剩5张票。
窗口2买票, 还剩4张票。
窗口1买票, 还剩5张票。
窗口4买票, 还剩3张票。
窗口2买票, 还剩2张票。
窗口1买票, 还剩1张票。
窗口3买票, 还剩2张票。
窗口4买票, 还剩0张票。
很明显, 窗口重复售卖了同一张票,是因为在下面这段代码中:
if (tickteCount > 0) {
// 有可能CPU在这个时候切换了,已经进入到另外一个线程里面。
tickteCount--;
System.out.println(Thread.currentThread().getName() + "买票, " + "还剩" + tickteCount + "张票。");
} else {
System.out.println("票卖完了");
}
线程1 可能已经进入到if里面了,但是CPU资源分配到其他线程,导致该线程停在这里,资源就会出现混乱。
解决方法:在synchronized(Object)里面写代码。
synchronized(Object),运行到该代码块的线程回去尝试获取Object的锁, 如果获取到了,就会执行synchronized{}里面的代码,否则就会不执行。
//SellTicket.java
import com.sun.org.apache.xml.internal.resolver.Catalog;
public class SellTicket implements Runnable {
private int tickteCount = 100;
private Object obj = new Object();
@Override
public void run() {
try {
while (tickteCount > 0) {
sellTicket();
// 让线程睡一会,以免太快把票卖完
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void sellTicket() {
synchronized (obj) { // 同步代码块 锁 任何一个对象
if (tickteCount > 0) {
tickteCount--;
System.out.println(Thread.currentThread().getName() + "买票, " + "还剩" + tickteCount + "张票。");
} else {
System.out.println("票卖完了");
}
}
}
}
运行结果:
窗口1买票, 还剩99张票。
窗口4买票, 还剩98张票。
窗口3买票, 还剩97张票。
窗口2买票, 还剩96张票。
窗口4买票, 还剩95张票。
窗口3买票, 还剩94张票。
窗口2买票, 还剩93张票。
窗口1买票, 还剩92张票。
窗口3买票, 还剩91张票。
窗口2买票, 还剩90张票。
窗口1买票, 还剩89张票。
窗口4买票, 还剩88张票。
窗口3买票, 还剩87张票。
窗口1买票, 还剩86张票。
窗口2买票, 还剩85张票。
窗口4买票, 还剩84张票。
窗口1买票, 还剩83张票。
窗口2买票, 还剩82张票。
窗口3买票, 还剩81张票。
窗口4买票, 还剩80张票。
窗口1买票, 还剩79张票。
窗口3买票, 还剩78张票。
窗口2买票, 还剩77张票。
窗口4买票, 还剩76张票。
窗口3买票, 还剩75张票。
窗口2买票, 还剩74张票。
窗口1买票, 还剩73张票。
窗口4买票, 还剩72张票。
窗口3买票, 还剩71张票。
窗口1买票, 还剩70张票。
窗口2买票, 还剩69张票。
窗口4买票, 还剩68张票。
窗口3买票, 还剩67张票。
窗口2买票, 还剩66张票。
窗口1买票, 还剩65张票。
窗口4买票, 还剩64张票。
窗口3买票, 还剩63张票。
窗口2买票, 还剩62张票。
窗口1买票, 还剩61张票。
窗口4买票, 还剩60张票。
窗口3买票, 还剩59张票。
窗口1买票, 还剩58张票。
窗口2买票, 还剩57张票。
窗口4买票, 还剩56张票。
窗口3买票, 还剩55张票。
窗口2买票, 还剩54张票。
窗口1买票, 还剩53张票。
窗口4买票, 还剩52张票。
窗口3买票, 还剩51张票。
窗口1买票, 还剩50张票。
窗口2买票, 还剩49张票。
窗口4买票, 还剩48张票。
窗口3买票, 还剩47张票。
窗口2买票, 还剩46张票。
窗口1买票, 还剩45张票。
窗口4买票, 还剩44张票。
窗口3买票, 还剩43张票。
窗口1买票, 还剩42张票。
窗口2买票, 还剩41张票。
窗口4买票, 还剩40张票。
窗口3买票, 还剩39张票。
窗口2买票, 还剩38张票。
窗口1买票, 还剩37张票。
窗口4买票, 还剩36张票。
窗口3买票, 还剩35张票。
窗口1买票, 还剩34张票。
窗口2买票, 还剩33张票。
窗口4买票, 还剩32张票。
窗口3买票, 还剩31张票。
窗口1买票, 还剩30张票。
窗口2买票, 还剩29张票。
窗口4买票, 还剩28张票。
窗口3买票, 还剩27张票。
窗口1买票, 还剩26张票。
窗口2买票, 还剩25张票。
窗口4买票, 还剩24张票。
窗口3买票, 还剩23张票。
窗口1买票, 还剩22张票。
窗口2买票, 还剩21张票。
窗口4买票, 还剩20张票。
窗口3买票, 还剩19张票。
窗口2买票, 还剩18张票。
窗口1买票, 还剩17张票。
窗口4买票, 还剩16张票。
窗口3买票, 还剩15张票。
窗口2买票, 还剩14张票。
窗口1买票, 还剩13张票。
窗口4买票, 还剩12张票。
窗口3买票, 还剩11张票。
窗口2买票, 还剩10张票。
窗口1买票, 还剩9张票。
窗口4买票, 还剩8张票。
窗口3买票, 还剩7张票。
窗口2买票, 还剩6张票。
窗口1买票, 还剩5张票。
窗口4买票, 还剩4张票。
窗口3买票, 还剩3张票。
窗口2买票, 还剩2张票。
窗口1买票, 还剩1张票。
窗口4买票, 还剩0张票。
另外,也可以在方法上面加上synchronized, 使得该方法变成一个同步方法,synchronized 获取的锁就是从本身这个对象的锁获取的。
private synchronized void sellTicket() {
// synchronized (obj) { // 同步代码块 锁 任何一个对象
if (tickteCount > 0) {
tickteCount--;
System.out.println(Thread.currentThread().getName() + "买票, " + "还剩" + tickteCount + "张票。");
} else {
System.out.println("票卖完了");
}
// }
}
二、线程之间的唤醒(三个方法必须写在synchronized代码块里面)
// Waiter.java
public class Waiter implements Runnable {
private Message message;// 使用message来加锁
public Waiter(Message message) {
this.message = message;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (message) {
try {
System.out.println(name + " 等待时间" + System.currentTimeMillis());
message.wait();
System.out.println(message.getMessage() + System.currentTimeMillis());
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//Notifier.java
public class Notifier implements Runnable{
Message message;
public Notifier(Message message) {
this.message = message;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized(message) {
message.setMessage("唤醒线程工作");
message.notify();
}
}
}
//Demo1.java
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Message message = new Message("锁");
Waiter waiter1 = new Waiter(message);
Waiter waiter2 = new Waiter(message);
Notifier notifier = new Notifier(message);
new Thread(waiter1, "waiter1").start();;
new Thread(waiter2, "waiter2").start();;
new Thread(notifier,"notifier").start();
new Thread();
}
}