线程的同步与死锁
1.1同步问题的引出
如果要想进行同步的操作,那么和明细就是多个线程需要访问统一资源
范例:以卖票为例
package day1;
class NewThread implements Runnable{//表示实现多线程
private int ticket = 5;
public void run() {//覆写run(),线程的主方法
for (int i = 0;i < 10; i++){
if(this.ticket > 0){
System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
}
}
}
}
public class TestRunnableDemo {
public static void main(String[] args) throws Exception {
NewThread myThread = new NewThread();
Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
t1.start();
t2.start();
t3.start();
}
}
线程A=5
线程C=3
线程B=4
线程C=1
线程A=2
于是下面开始挖掘本程序所存在的问题。
范例:观察程序的问题
package day1;
class NewThread implements Runnable{//表示实现多线程
private int ticket = 5;
public void run() {//覆写run(),线程的主方法
for (int i = 0;i < 10; i++){
if(this.ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
}
}
}
}
public class TestRunnableDemo {
public static void main(String[] args) throws Exception {
NewThread myThread = new NewThread();
Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
t1.start();
t2.start();
t3.start();
}
}
线程A=5
线程B=5
线程C=4
线程C=3
线程B=2
线程A=1
线程B=0
线程C=-1
此时就观察出程序的问题所在了,出现了负数。
这样的 问题就属于线程的不同步操作,所以发现多个线程操作时必须要考虑资源的同步问题。
1.2 实现同步操作
整个代码之中发现一个逻辑上的错误。以上程序中,将判断是否有票、延迟、买票分为了三个部分。那么实际上每一个线程如果要执行卖票,其他线程应该等待当前线程执行完毕后才可以进入。解决问题
如果要想在若干行代码上实现锁这个概念,那么就需要通过使用同步代码块或同步方法解决。
1.同步代码块
使用synchronized关键字定义的代码块就称为同步代码块,但是在进行同步的时候需要设置有一个同步对象,那么往往可以使用this同步当前对象。
package day1;
class NewThread implements Runnable{//表示实现多线程
private int ticket = 50;
public void run() {//覆写run(),线程的主方法
for (int i = 0;i < 100; i++){
synchronized (this){
if(this.ticket > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
}
}
}
}
}
public class TestRunnableDemo {
public static void main(String[] args) throws Exception {
NewThread myThread = new NewThread();
Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
t1.start();
t2.start();
t3.start();
}
}
加入同步之后整个的代码执行的速度已经变慢了,而且不像没有同步的时候那样,多个线程会一起进入到方法之中。
异步的执行速度要快于同步的执行速度,但是异步的操作属于非线程安全的操作,而同步操作属于线程的安全操作。
2.同步方法
但是对于同步操作,除了了用于代码块定义外,也可以在方法上定义同步操作。
package day1;
class NewThread implements Runnable{//表示实现多线程
private int ticket = 50;
public void run() {//覆写run(),线程的主方法
for (int i = 0;i < 100; i++) {
this.sall();
}
}
public synchronized void sall(){
if(this.ticket > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
}
}
}
public class TestRunnableDemo {
public static void main(String[] args) throws Exception {
NewThread myThread = new NewThread();
Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
t1.start();
t2.start();
t3.start();
}
}
在多个线程访问同一资源时一定要考虑到数据同步问题,同步就使用synchronize关键字。
1.3 死锁分析
死锁是一种不确定的状态,对于死锁的操作应该出现的越少越好,下面的代码只是一个死锁的演示,代码不做任何的实际意义。
package day1;
class QiangDao{
public synchronized void say(YouQianRen yqr){
System.out.println("强盗说:给我3000万美金,还你儿子。");
yqr.get();
}
public synchronized void get(){
System.out.println("强盗得到了3000万美金放了那个还在。");
}
}
class YouQianRen{
public synchronized void say(QiangDao qd){
System.out.println("有钱人:先放了我儿子,在考虑给你3000万美金。");
qd.get();
}
public synchronized void get(){
System.out.println("有钱人找回了儿子。");
}
}
public class TestDemo implements Runnable{
private QiangDao qd = new QiangDao();
private YouQianRen yqr = new YouQianRen();
public TestDemo(){
new Thread(this).start();
yqr.say(qd);
}
public static void main(String[] args) {
new TestDemo();
}
@Override
public void run() {
qd.say(yqr);
}
}
输出
出现死锁,程序没有执行完。
面试题:请问多个线程访问同意资源是可能带来什么问题?以及会产生什么样的附加问题?
1.多个线程访问统一资源是必须要考虑同步,可以使用synchronize定义同步代码块或同步方法;
2.程序中如果出现过多的同步那么就将产生死锁。
总结:如果看到synchronize声明的方法,一定要记住,这是一个同步方法,属于线程安全的操作,但是性能不会特别的高。