线程的同步与死锁
在多线程的开发中,同步与死锁的概念非常重要,需要明白:
1.哪里需要同步
2.如何实现同步,代码了解即可
3.以及实现同步之后会有哪些副作用,了解代码并不要求完整的编写,但概念必须清楚。
具体内容
问题的引出:以买火车票为例,不管任何购买方式,一趟接车的票数是固定的。
public class synRunnable {
public static void main(String[] args) {
myThread mt = new myThread(); //定义线程对象
Thread t1 = new Thread(mt); //定义Thread对象
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.start();
t2.start();
t3.start();
}
}
class myThread implements Runnable{
private int ticket = 5; //设一同有五张票
public void run() {
for(int i = 0;i < 100;i++) {
if(ticket > 0) {
try {
Thread.sleep(300);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(“买票:ticket = ” + ticket–);
}
}
运行结果:买票:ticket = 5
买票:ticket = 4
买票:ticket = 3
买票:ticket = 2
买票:ticket = 1
买票:ticket = 0
买票:ticket = -1
出现错误,如果想解决此类问题,必须使用线程同步
同步代码块
之前已介绍过了代码块分为四种:
1.普通代码块,是直接定义在方法之中
2.代码块:是直接定义在类中的,优先于构造方法,重复调用
3.静态块:是使用static关键字声明,优先于构造块执行
在代码块上加上“synchronized”关键字的话,则此代码块就称为同步代码块
格式:
synchronized(同步对象){
需要同步的对象
}
同步的时候必须指明同步的对象,一般情况下会将当前对象作为同步对象,使用this表示。
public class synRunnable {
public static void main(String[] args) {
myThread mt = new myThread(); //定义线程对象
Thread t1 = new Thread(mt); //定义Thread对象
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.start();
t2.start();
t3.start();
}
}
class myThread implements Runnable{
private int ticket = 5; //设一同有五张票
public void run() {
for(int i = 0;i < 100;i++) {
synchronized(this) {
if(ticket > 0) {
try {
Thread.sleep(300);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(“买票:ticket = ” + ticket–);
}
}
}
}
}
运行结果:
买票:ticket = 5
买票:ticket = 4
买票:ticket = 3
买票:ticket = 2
买票:ticket = 1
从运行结果来看,加入同步操作,所以不会产生负数出现的情况,但是运行效率明显降低
同步方法
除了可以将需要的代码设置成同步代码块之外,也可以使用synchronized关键字将一个方法声明成同步方法
格式:
synchronized 方法返回值 方法名称(参数列表){}
public class synRunnable {
public static void main(String[] args) {
myThread mt = new myThread(); //定义线程对象
Thread t1 = new Thread(mt); //定义Thread对象
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.start();
t2.start();
t3.start();
}
}
class myThread implements Runnable{
private int ticket = 5; //设一同有五张票
public void run() {
for(int i = 0;i < 100;i++) {
this.sale(); //调用同步方法
}
}
public synchronized void sale() { //声明同步方法
if(ticket > 0) {
try {
Thread.sleep(300); //加入延迟
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("买票:ticket = " + ticket--);
}
}
}
运行效果和上面一样,所以证明两种方法效果一样。
死锁
**程序中过多的操作会产生死锁
资源共享时需要进行同步操作**
public class ThreadDeadLock implements Runnable {
private static Zhangsan zs = new Zhangsan(); //实例化static型对象
private static Lisi ls = new Lisi(); //实例化static型对象
private boolean flag = false;
public void run() {
if(flag) {
synchronized(zs) {
zs.say();
try {
Thread.sleep(500);
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized(ls) {
zs.get();
}
}
}else {
synchronized(ls) {
ls.say();
try {
Thread.sleep(500);
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized(zs) {
ls.get();
}
}
}
}
public static void main(String[] args) {
ThreadDeadLock t1 = new ThreadDeadLock();
ThreadDeadLock t2 = new ThreadDeadLock();
t1.flag = true;
t2.flag = false;
Thread thA = new Thread(t1);
Thread thB = new Thread(t2);
thA.start();
thB.start();
}
}
class Zhangsan{
public void say() {
System.out.println(“张三对李四说:你给我画,我就把书给你”);
}
public void get() {
System.out.println("张三得到画了");
}
}
class Lisi{
public void say() {
System.out.println(“李四对张三说:你给我书,我就把画给你”);
}
public void get() {
System.out.println(“李四得到书了”);
}
}
运行结果:
张三对李四说:你给我画,我就把书给你
李四对张三说:你给我书,我就把画给你
以上就是死锁状态,都在互相等待着对方的回答
总结
1.多个线程在访问同一资源需要进行同步操作
2.同步使用synchronized关键字完成,分为同步代码块和同步方法
3.过多的同步有可能造成死锁的产生,死锁是在程序运行时的一种表现状态,只要了解其产生原理即可