Lock锁:
ReentrantLock实现了Lock接口,所以可以称为Lock锁
ReentrantLock与synchronized的区别:
1、实现原理不同:ReentrantLock是一种代码层面的控制实现,而synchronized是关键字,依靠的是底层编译后的指令实现
2、加锁范围不同:ReentrantLock只能对某一段代码块加锁,而synchronized可以对代码块和方法加锁
3、加锁释放锁方式不同:ReentrantLock需要手动的加锁释放锁,而synchronized是隐式的自动的加锁,自动释放锁(代码执行完了,出现异常了)
示例:模拟售票(使用Lock锁)
public class TicketThread extends Thread{
static int num = 100;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while(true){
/*
使用try{}finally{}的原因:
Lock锁是手动添加及释放的,如果加锁后的代码出现异常,就不会释放锁。
使用try{}finally{}之后,将释放锁的代码放入finally{}的代码块中,不管try{}代码块中是否出现异常都会释放锁。
*/
try{
lock.lock();//加锁
if(num>0){
System.out.println(Thread.currentThread().getName()+":"+num);
num--;
}else{
break;
}
}finally {
lock.unlock();//释放锁
}
}
}
}
线程死锁:
不同的线程分别占有对方需要的同步资源不放,双方相互等待,程序不会报错,一直卡着。
eg:自己制造一个线程死锁,但是不确保一定能死锁,因为线程的调用由系统绝对。
public class DieLockThread extends Thread{
static Object objA = new Object();
static Object objB = new Object();
boolean flag;
public DieLockThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag){
synchronized (objA){
System.out.println("if objA");
synchronized (objB){
System.out.println("if objB");
}
}
}else{
synchronized (objB){
System.out.println("else objB");
synchronized (objA){
System.out.println("else objA");
}
}
}
}
}
public class Test {
public static void main(String[] args) {
DieLockThread t1 = new DieLockThread(true);
t1.start();
DieLockThread t2 = new DieLockThread(false);
t2.start();
}
}
线程通信:
会使用到三个方法:
wait();是Object类中定义的方法,必须是使用同步对象来调用,让线程等待,释放锁,等待的线程不能自己醒来,必须让其他线程唤醒
notify();是Object类中定义的方法,必须是使用同步对象来调用,唤醒等待的线程
notifyAll();是Object类中定义的方法,必须是使用同步对象来调用,用来唤醒所有被等待的线程
三个方法都必须在同步代码块中执行。
示例1:两个线程交替打印1-100的数字
public class PrintNumThread extends Thread{
static int num = 1;
static Object object = new Object();
@Override
public void run() {
while(num<=100){
synchronized(object){
object.notify();//唤醒等待的线程
System.out.println(Thread.currentThread().getName()+":"+num);
num++;
try {
if(num<=100){
object.wait();//让线程等待,释放锁,等待的线程不能自己醒来,必须让其他线程唤醒
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class Test {
public static void main(String[] args) {
PrintNumThread p1 = new PrintNumThread();
p1.start();
PrintNumThread p2 = new PrintNumThread();
p2.start();
}
}
sleep()和wait()的共同点与区别:
共同点:都可以让线程进入阻塞状态
区别:sleep();让线程阻塞指定的时间,时间到了后,自己唤醒进入到就绪状态,不会释放同步锁,是Thread类中的方法。
wait();让线程进入等待(阻塞),不会自己唤醒,必须通过notify()、notifiAll()唤醒,等待时,会释放同步锁,是Object类中的方法。
示例2:生产者与消费者问题
生产者(Productor)将产品放在柜台(Counter),而消费者(Customer)从柜台 处取走产品,生产者一次只能生产固定数量的产品(比如:1), 这时柜台中不能 再放产品,此时生产者应停止生产等待消费者拿走产品,此时生产者唤醒消费者来 取走产品,消费者拿走产品后,唤醒生产者,消费者开始等待.
public class Counter {
int num = 0;//共享商品数量
/*
生产者生产商品
*/
public synchronized void add(){
if(num==0){
this.num=1;
this.notify();//唤醒消费者
System.out.println("生产者生产了1件商品");
}else{
try {
this.wait();//生产者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
消费者取走商品
*/
public synchronized void sub(){
if(num>0){
this.num=0;
this.notify();//唤醒生产者
System.out.println("消费者取走了1件商品");
}else {
try {
this.wait();//消费者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Productor extends Thread {
/*
生产者线程
*/
Counter counter ;
public Productor(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter.add();
}
}
}
public class Customer extends Thread{
/*
消费者线程
*/
Counter counter ;
public Customer(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter.sub();
}
}
}
public class Test {
public static void main(String[] args) {
Counter counter = new Counter();
Productor p = new Productor(counter);
Customer c = new Customer(counter);
p.start();
c.start();
}
}
创建线程的第三种方法:
实现Callable接口
由于创建线程时,需要添加实现Runnable接口的任务,Callable接口没有实现Runnable接口,不能直接创建线程,所以需要用FutureTask来接收任务,FutureTask实现了Runnable接口,因此可以间接用来创建线程。
import java.util.concurrent.Callable;
public class SumTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int num = 0;
for (int i = 0; i < 10; i++) {
num += i ;
}
return num;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) {
SumTask sumTask = new SumTask();//创建任务
FutureTask<Integer> futureTask = new FutureTask(sumTask);//接收任务
Thread thread = new Thread(futureTask);//创建线程
thread.start();//启动线程
try {
Integer integer = futureTask.get();//获取线程call()的返回值
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}