Java----线程的同步
引入问题
在关于线程的操作时,往往会遇到线程的安全问题。
这里我用一个例子来说明:
package com.CharlesLC_Test;
public class Window_Test {
public static void main(String[] args) {
Window window = new Window();
Thread window1 = new Thread(window);
Thread window2 = new Thread(window);
Thread window3 = new Thread(window);
window1.setName("窗口一");
window2.setName("窗口二");
window3.setName("窗口三");
window1.start();
window2.start();
window3.start();
}
}
class Window implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":成功取票"+tickets+"还剩下" + --tickets);
}
else{break;}
}
}
}
结果:发现窗口出现了重票
问题所在:
对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
线程同步的方式
使用Synchronized.
当一个线程调用对象的一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,执行结束之后,释放锁。当一个线程拿到这个锁时,其他线程不能进入被这个锁起来的这段代码。
同步代码块
使用方法:
synchronized(对象){//需要被同步的代码;}
这里我修改了取票这个例子中的Window类:
class Window implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true) {
// 这里作了改变,把这段代码用 synchronized锁了起来
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":成功取票" + tickets + "还剩下" + --tickets);
} else {
break;
}
}
}
}
}
结果:
同步方法:
使用方法:
public synchronized void show(String name){ }
修改过的Window类
class Window implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true) {
show();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets == 0){
break;
}
}
}
private synchronized void show() {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ":成功取票" + tickets + "还剩下" + --tickets);
}
}
}
结果:
- 当前线程的同步方法、同步代码块执行结束。
- 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
- 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
- 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
注意:必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就无法保证共享资源的安全
使用Lock()
导入
import java.util.concurrent.locks.ReentrantLock;
使用方式:
class A{
private final ReentrantLock lock=new ReenTrantLock();
public void m(){
lock.lock()
try{
//保证线程安全的代码;
finally{
lock.unlock();
}
synchronized与Lock的对比
1.Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
2.Lock只有代码块锁,synchronized有代码块锁和方法锁
3.使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)