Java 多线程 —— 同步代码块 (1),狂神说 docker 进阶笔记
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){
this.flag = flag;
}
@Override
public void run() {
if(flag){
synchronized(LockObject.lock1){
System.out.println("lock1");
synchronized(LockObject.lock2){
System.out.println("lock2");
}
}
}else{
synchronized(LockObject.lock2){
System.out.println("lock2");
synchronized(LockObject.lock1){
System.out.println("lock1");
}
}
}
}
}
package test.MyThread.ticketDemo;
public class DieLockDemo {
public static void main(String[] args) {
DieLockThread d1 = new DieLockThread(true);
DieLockThread d2 = new DieLockThread(false);
d1.start();
d2.start();
}
}
程序会卡在这一步,不能进行下一步也不能停止
利用有参构造,构造出来的线程 d1 应该是先获得锁对象 LockObject.lock1 然后执行打印语句。接着获取锁对象 LockObject.lock2,然后打印 lock2。
但是这里因为线程 d2 是先获取的锁对象 LockObject.lock2,并占据这个锁对象,然后想获得锁对象 LockObject.lock1,但 LockObject.lock1 此时被线程 d1 占据着
两个线程都在等待对方释放锁对象,然后进行下一步,但是两者都不释放,导致程序卡死在这里。这就造成了死锁。
lock
package test.MyThread.ticketDemo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockThread implements Runnable{
private int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run(){
while(ticket>0){
try{
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 正在出售第 " + (ticket--) + " 张票");
}
}finally {
lock.unlock();
}
}
}
}
评论