}
System.out.println(Thread.currentThread().getName() + “:卖票,票号为:” + ticket);
ticket–;
}
}
}
3.4 利用Runnable实现线程同步(Lock锁)
3.4.1 Lock锁定义
-
从 JDK 5.0开始,Java 提供了更强大的线程同步机制 通过显式定义同步锁对象来实现同步,同步锁使用 Lock 对象充当 。
-
java.util.concurrent.locks.Lock 接口是控制多个线程对共享资源进行访问的工具。 锁提供了对共享资源的独占访问,每次只能有一个线程对 Lock 对象加锁,线程开始访问共享资源之前应先获得 Lock 对象 。
-
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义, 在 实现线程安全的控制中,比较常用的是 ReentrantLock 可以显式加锁、释放锁。
3.4.2 代码实例
import java.util.concurrent.locks.ReentrantLock;
/**
-
解决线程安全问题的方式三:Lock锁 — JDK5.0新增
-
- 面试题:synchronized 与 Lock的异同?
-
相同:二者都可以解决线程安全问题
-
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
-
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
-
2.优先使用顺序:
-
Lock 同步代码块(已经进入了方法体,分配了相应资源) 同步方法(在方法体之外)
*/
public class Lock {
public static void main(String[] args) {
Window3 w = new Window3();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName(“窗口1”);
t2.setName(“窗口2”);
t3.setName(“窗口3”);
t1.start();
t2.start();
t3.start();
}
}
class Window3 implements Runnable{
private int ticket = 100;
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{//2.调用锁定方法lock()
lock.lock();
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “:售票,票号为:” + ticket);
ticket–;
}else{
break;
}
}finally {
//3.调用解锁方法:unlock()
lock.unlock();
}
}
}
}
3.5 synchronized同步与Lock锁的区别
相同点:二者都可以解决线程安全问题。
不同点:
-
synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器,Lock 是显式锁(需要手动开启和关闭锁), synchronized 是隐式锁,出了作用域自动释放;
-
Lock 只有代码块锁, synchronized 有代码块锁和方法锁;
-
使用 Lock 锁, JVM 将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)。
使用的优先顺序:Lock锁、synchronized同步代码块、synchronized同步方法。
同步虽然解决了线程的安全问题,但其实操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。
3.6 死锁问题
3.6.1 定义
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
-
出现死锁后,不会出现异常,不会出现提示,只是所的线程都处于阻塞状态,无法继续
-
我们使用同步时,要避免出现死锁。
3.6.2 解决办法
-
专门的算法、原则
-
尽量减少同步资源的定义
-
尽量避免嵌套同步
3.6.3 死锁的实例
//死锁的演示;死锁的增强版
class A {
public synchronized void foo(B b) { //同步监视器:A类的对象:a
System.out.println("当前线程名: " + Thread.currentThread().getName()
- " 进入了A实例的foo方法"); //
System.out.println("当前线程名: " + Thread.currentThread().getName()
- " 企图调用B实例的last方法"); // ③
b.last();
}
public synchronized void last() {//同步监视器:A类的对象:a
System.out.println(“进入了A类的last方法内部”);
}
}
class B {
public synchronized void bar(A a) {//同步监视器:b
System.out.println("当前线程名: " + Thread.currentThread().getName()
- " 进入了B实例的bar方法"); // ②
System.out.println("当前线程名: " + Thread.currentThread().getName()
- " 企图调用A实例的last方法"); // ④
a.last();
}
public synchronized void last() {//同步监视器:b
System.out.println(“进入了B类的last方法内部”);
}
}
public class DeadLock implements Runnable {
A a = new A();
B b = new B();
public void init() {
Thread.currentThread().setName(“主线程”);
// 调用a对象的foo方法
a.foo(b);
System.out.println(“进入了主线程之后”);
}
@Override
public void run() {
Thread.currentThread().setName(“副线程”);
// 调用b对象的bar方法
b.bar(a);
System.out.println(“进入了副线程之后”);
}
public static void main(String[] args) {
DeadLock dl = new DeadLock();
new Thread(dl).start();
dl.init();
}
}
4 继承Thread类
=======================