线程的并发控制
**并发:**同一个对象多个线程同时操作。
线程同步
- 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。
- 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized),当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
线程同步方式:
-
同步块
synchronized (obj){}//其中obj称为同步监视器obj可以是任何对象,但是推荐使用共享资源作为同步监视器;同步方法中无需指定同步监视器,因为同步方法的同步监视器是this即该对象本身,或class即类的模子
-
同步方法。
public synchronized void method(int args) {}synchronized 方法控制对“成员变量|类变量”对象的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。
线程同步的问题:
- 一个线程持有锁会导致其它所有需要此锁的线程挂起.
- 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题.
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题.
- 若将一个大的方法声明为synchronized 将会大大影响效率 。
代码实现:
package io.github.thread;
import java.util.ArrayList;
import java.util.List;
public class MyCinema {
public static void main(String[] args) {
List<Integer> seat=new ArrayList<Integer>();
seat.add(1);
seat.add(2);
seat.add(3);
seat.add(4);
seat.add(5);
List<Integer> cut1=new ArrayList<Integer>();
cut1.add(1);
cut1.add(2);
cut1.add(3);
cut1.add(4);
List<Integer> cut2=new ArrayList<Integer>();
cut2.add(5);
cut2.add(2);
Cinema cinema=new Cinema(seat,"影院");
new Thread(new Customer(cinema, cut1, "hu")).start();
new Thread(new Customer(cinema, cut2, "li")).start();
}
}
class Cinema{
private List<Integer> list;//可用位置
private String name;
public Cinema(List<Integer> list, String name) {
super();
this.list = list;
this.name = name;
}
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Customer implements Runnable{
Cinema cinema;
List<Integer> seats;//要定的座位
String name;
public Customer(Cinema cinema, List<Integer> seats, String name) {
super();
this.cinema = cinema;
this.seats = seats;
this.name = name;
}
@Override
public void run() {
ReservationLocation(seats);
}
//订票
public boolean ReservationLocation(List<Integer> seats) {
System.out.println(name+"先生,您要预定的位置是"+seats);
/************************锁机制****************************/
synchronized (cinema) {
System.out.println("可用座位为:" + cinema.getList());
List<Integer> copy = new ArrayList<>();
copy.addAll(cinema.getList());
boolean flag=copy.containsAll(seats);
if (flag) {
copy.removeAll(seats);
cinema.setList(copy);
System.out.println(name+"先生,位置" + seats + "预定成功,剩余座位为:" + cinema.getList());
return true;
}
System.out.println("抱歉"+name+",订票失败");
return false;
}
/*********************************************************/
}
}
运行结果:
如果上述程序去掉synchronized(锁机制)的嵌套的话,运行结果可能如下:
由上图可以看出,hu先生和li先生都订票成功了,但票号是不对的,因此可以看出该线程运行是不安全的。
如果加上锁机制,如上完整程序,运行结果可能如下图。
如上图所示,该线程是安全的。
死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。
代码实现
package io.github.thread;
public class DeadLock {
public static void main(String[] args) {
Knife knife=new Knife();
Fork fork=new Fork();
Eat eat=new Eat("hu",1);
Eat eat2=new Eat("li",0);
new Thread(eat).start();
new Thread(eat2).start();
}
}
class Knife{
}
class Fork{
}
class Eat implements Runnable{
static private Knife knife=new Knife();
static private Fork fork=new Fork();
private String people;
private int chiose;
public Eat(String people, int chiose) {
super();
this.people = people;
this.chiose = chiose;
}
@Override
public void run() {
if(chiose==0) {
synchronized (knife) {
System.out.println(people+"使用刀");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(fork) {
System.out.println(people+"使用叉子");
}
}
}else {
synchronized (fork) {
System.out.println(people+"使用叉子");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(knife) {
System.out.println(people+"使用刀");
}
}
}
}
}
避免死锁问题,尽力减少synchronized嵌套!!!