1.synchronized同步关键字
这里涉及到多个线程对共享资源进行处理,这里就必须要给线程“排队”,使用的关键字是synchronized(同步),在程序中,给对共享资源进行处理的代码部分(整个方法或代码块)添加此关键字。相当于给代码块加锁,取得钥匙的线程对资源操作,完成后将钥匙交予其他线程,这保证了同一个资源某一时间内只有一个线程可以对其修改等操作。
同步代码块
synchronized(lock){
操作共享资源代码块
}
lock是一个锁对象,它是同步代码块的关键。当线程执行同步代码块时,首先会检查锁对象的标志位,默认情况下标志位为1,此时线程会执行同步代码块,同时将锁对象的标志位置为0,当一个新的线程执行到这段同步代码块时,由于锁对象的标志位为0,新线程会发生阻塞,等待当前线程执行完同步代码块后,锁对象的标志位被置为1,新线程才能进入同步代码块执行其中的代码。循环往复,直到共享资源被处理完为止。这个过程就好比一个公用电话亭,只有前一个人打完电话出来后,后面的人才可以打。
注意:
- 同步代码块中的锁对象可以是任意类型的对象,但多个线程共享的锁对象必须是唯一的。“任意”说的是共享锁对象的类型。所以,锁对象的创建代码不能放到run()方法中,否则每个线程运行到run()方法都会创建一个新对象,这样每个线程都会有一个不同的锁,每个锁都有自己的标志位。线程之间便不能产生同步的效果。
public void run(){
Object lock = new Object();
//代码
synchronized(lock){
//锁失去了意义
//对共享资源处理代码
}
//代码
}
- 注意细分锁。可以有多个锁对象,对不同的资源“上锁”
class A{
private Object lock1 = new Object();
private Object lock2 = new Object();
void f(){
synchronized(lock1){
//访问资源P
}
}
void g(){
synchronized(lock2){
//访问资源Q
}
}
}
下面来一个买票的例子:
//定义Ticket1类继承Runnable接口
class Ticket1 implements Runnable {
private int tickets = 10; // 定义变量tickets,并赋值10
**Object lock = new Object(); // 定义任意一个对象,用作同步代码块的锁**
public void run() {
while (true) {
**synchronized (lock) {
// 定义同步代码块**
try {
Thread.sleep(10); // 经过的线程休眠10毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0) {
System.out.println(Thread.currentThread().getName()
+ "---卖出的票" + tickets--);
} else {
// 如果 tickets小于0,跳出循环
break;
}
}
}
}
}
public class Example {
public static void main(String[] args) {
Ticket1 ticket = new Ticket1(); // 创建Ticket1对象
// 创建并开启四个线程
new Thread(ticket, "线程一").start();
new Thread(ticket, "线程二").start();
new Thread(ticket, "线程三").start();
new Thread(ticket, "线程四").start();
}
}
运行结果:
同步方法
格式:synchronized返回值类型 方法名([参数1, … ] ){}
被synchronized修饰的方法在某一时刻只允许一个线程访问,访问该方法的其他线程都会发生阻塞,直到当前线程访问完毕后,其他线程才有机会执行方法。
// 定义Ticket1类实现Runnable接口
class Ticket1 implements Runnable {
private int tickets = 10;
public void run() {
while (true) {
saleTicket(); // 调用售票方法
if (tickets <= 0) {
break;
}
}
}
// 定义一个同步方法saleTicket()
private synchronized void saleTicket() {
if (tickets > 0) {
try {
Thread.sleep(10); // 经过的线程休眠10毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---卖出的票"
+ tickets--);
}
}
}
public class Example {
public static void main(String[] args) {
Ticket1 ticket = new Ticket1(); // 创建Ticket1对象
// 创建并开启四个线程
new Thread(ticket,"线程一").start();
new Thread(ticket,"线程二").start();
new Thread(ticket,"线程三").start();
new Thread(ticket,"线程四").start();
}
}