Java 多线程 —— 同步代码块,给大家安排上
必须有两个或两个以上的线程
同一时间只有一个线程能够执行同步代码块
多个线程想要同步时,必须共用同一把锁
synchronized(对象)括号里面的对象就是一把锁
使用 synchronized 的过程:
只有抢到锁的线程才可以执行同步
代码块,其余的线程即使抢到了 CPU 执行权,也只能等待,等待锁的释放。
代码执行完毕或者程序抛出异常都会释放锁,然后还未执行同步代码块的线程争抢锁,谁抢到谁就能运行同步代码块。
[](
)同步代码块
因此,修改后的代码为:
package test.MyThread.ticketDemo;
public class RunnableThread implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run(){
while(true){
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第 " + ticket + " 张票");
ticket--;
}
}
}
}
}
package test.MyThread.ticketDemo;
public class ticketDemo1 {
public static void main(String[] args) {
//这里没有改动,只是在上一个代码中加了一把锁
RunnableThread r1 = new RunnableThread();
Thread t1 = new Thread(r1,"窗口一");
Thread t2 = new Thread(r1,"窗口二");
Thread t3 = new Thread(r1,"窗口三");
t1.start();
t2.start();
t3.start();
}
}
可以看出来结果符合我们的预期,是正确的
现在又有了新的问题,那就是如果我在构造线程的 RunnableThread 类里面加入方法呢?同步代码块里面出现方法时,我们应该怎么“上锁”呢?
[](
)同步方法(this 锁)
[](
)同步方法,在 public 的后面加上 synchronized 关键字
package test.MyThread.ticketDemo;
public class RunnableThread1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
public boolean flag = true;
@Override
public void run(){
if(flag==true){
while(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
SellTicket1();
}
}
}
//同步方法,在 public 的后面加上 synchronized 关键字
public synchronized void SellTicket1(){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"正在出售第 "+ticket+" 张票");
ticket--;
}
}
}
package test.MyThread.ticketDemo;
public class ticketDemo2 {
public static void main(String[] args) throws InterruptedException {
RunnableThread1 r = new RunnableThread1();
Thread t1 = new Thread(r,"窗口一");
Thread t2 = new Thread(r,"窗口二");
t1.start();
t2.start();
}
}
[](
)this 锁
先来看看,如果有两条路径,一条路径是使用同步代码块,但是对象是 obj,另一条路径是使用同步方法
package test.MyThread.ticketDemo;
public class TicketWindow2 implements Runnable{
//定义 100 张票
private static int tickets = 100;
Object obj = new Object();
int i =0;
@Override
public void run() {
while (true){
if(i%2==0){
synchronized (obj){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
}
}
}else {
sellTicket();
}
i++;
}
}
public synchronized void sellTicket(){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
}
}
}
结果出错,说明同步方法的用的对象锁不能是任意的对象,不同的线程应该用相同的锁。同步方法是属于对象,而在这个类里面调用方法的是 this 对象,也就是 this.sellTicket(),因此把 this 提取出来作为对象锁中的对象。这样多个线程都用的是 this 锁
package test.MyThread.ticketDemo;
public class TicketWindow2 implements Runnable{
//定义 100 张票
private static int tickets = 100;
Object obj = new Object();
int i =0;
@Override
public void run() {
while (true){
if(i%2==0){
synchronized (this){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
}
}
}else {
sellTicket();
}
i++;
}
}
public synchronized void sellTicket(){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
}
}
}
修改完成后再运行代码,发现没有错误
注:
一个线程使用同步方法,另一个线程使用同步代码块 this 锁,可以实现同步
一个线程使用同步方法,另一个线程使用同步代码块,但是不是 this 锁。这种情况不能实现同步。
[](
)静态同步方法
同步方法的锁对象是 this,
静态同步方法的锁对象是:这个静态同步方法所属的类的字节码文件
下面代码挺长的,但其实就修改了上面同步方法的代码的两处地方
public synchronized void sellTicket(){}改为
public synchronized static void sellTicket(){}
synchronized (this){}改为 synchronized (TicketWindow2.class){}
package test.MyThread.ticketDemo;
public class TicketWindow2 implements Runnable{
//定义 100 张票
private static int tickets = 100;
Object obj = new Object();
int i =0;
@Override
public void run() {
while (true){
if(i%2==0){
synchronized (TicketWindow2.class){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
}
}
}else {
sellTicket();
}
i++;
}
}
public synchronized static void sellTicket(){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
}
}
}
main()方法里面创建进程和启动进程的代码,和上面同步方法里面的代码相同
结果也和上面的一样,都不再列出来了
[](
)死锁问题
package test.MyThread.ticketDemo;
//两个不同的锁对象
public class LockObject {
public static final Object lock1 = new Object();
public static final Object lock2 = new Object();
}
package test.MyThread.ticketDemo;
public class DieLockThread extends Thread{
public boolean flag;
public DieLockThread(boolean flag){
评论