您的当前位置:首页正文

线程中的资源共享和同步

2024-11-28 来源:个人技术集锦

<div class="iteye-blog-content-contain" style="font-size: 14px"></div>

 这个总结将对线程中的资源共享和线程同步问题进行简单的分析。

线程:是进程中的一个单一的连续控制流程。

一个进程可以含有多个线程,那下面就谈谈多线程。
java中多线程的实现有两种手段:1 继承Thread类  2实现Runnable接口
这里以买火车票为例。

class Ticket extends Thread {
	public int tickets = 10;
	public String name;
	public Ticket(String name){
		this.name = name;
	}
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println(this.name+"买票"+this.tickets--);
		}
	}
}

public class Test1 {
	public static void main(String []arg){
		Ticket t1 = new Ticket("线程1");
		Ticket t2 = new Ticket("线程2");
		Ticket t3 = new Ticket("线程3");
		
		t1.run();
		t2.run();
		t3.run();
	}
}

 看到运行结果了吧,它们都是顺序执行的,这说明我们的调用方法不对,当我们把run方法改为start方法后才出现我们想要的结果.那我们来看看run方法和start方法的区别。

run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用;
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;

run()方法只是一个普通的方法,如果直接调用run方法的话那程序中还是只有一个主线程,并没有达到多线程的目的,主程序中还是会顺序执行,执行完一个run方法后才会继续执行下一个方法。而start()方法是负责启动一个线程,不用等待执行完其中的run方法后再执行下面的方法,而是直接执行下面的方法。简单的说就是你在run方法中写入要执行的代码,然后你只要调用start方法就可以了,至于什么时候执行run方法,那就不是你负责的了。start()方法的功能其实是向cpu申请另一个线程空间来执行run()方法的,它和当前的线程空间是相对独立的。就是说如果你直接调用run方法的话它仍然会执行,但是在当前的线程空间,所以它会按顺序执行,而调用start方法后,run方法就会和当前的代码并行的执行,从而达到多线程。

另一个我们要考虑的问题就是资源共享问题。这个问题也可以用来区别Thread和Runnable,而多线程在很多情况下都是处理资源共享的问题。下面我们仍以买火车票为例来看看资源共享的问题。

class Ticket extends Thread {
	public int tickets = 10;
	public String name;

	public void run() {
		while (this.tickets > 0) {
			System.out.println(this.name + " 卖票:" + this.tickets--);
		}
	}

	public Ticket(String name) {
		this.name = name;
	}
}

public class Test1 {
	public static void main(String[] arg) {
		Ticket t1 = new Ticket("线程1");
		Ticket t2 = new Ticket("线程2");
		Ticket t3 = new Ticket("线程3");

		t1.start();
		t2.start();
		t3.start();

	}
}

 对于这个的运行结果我们之前已经试过了,从结果上看,它虽然实现了多线程,但它好像并不符合实际,我们定义一共有10张票,分三个地方来买,那就是说三个地方共享这10张票,可结果出现了30个数,显然是不对的,那如果把代码改一下:

class Ticket implements Runnable {
	public int tickets = 10;
	public String name;

	public void run() {
		while (this.tickets > 0) {
			System.out.println("卖票:" + this.tickets--);
		}
	}
}

public class Test1 {
	public static void main(String args[]) {
		// 准备四个售票点
		Demo d = new Demo();

		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		Thread t3 = new Thread(d);
		
		t1.start();
		t2.start();
		t3.start();
	}
}

 再运行,结果:

卖票:10
卖票:9
卖票:8
卖票:7
卖票:6
卖票:5
卖票:4
卖票:3
卖票:2
卖票:1

资源共享的问题也算是解决了。
还有一个问题就是线程同步的问题,这也是值得我们注意的问题,把上面的代码再进行修改:

class Demo implements Runnable {
	private int ticket = 10;

	public void run() {
		while (this.ticket > 0) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("卖票:" + this.ticket--);
		}
	}
};

public class Test1 {
	public static void main(String args[]) {
		// 准备四个售票点
		Demo d = new Demo();

		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		Thread t3 = new Thread(d);
		
		t1.start();
		t2.start();
		t3.start();
	}
};

 运行结果:
卖票:10
卖票:9
卖票:8
卖票:7
卖票:6
卖票:5
卖票:4
卖票:3
卖票:2
卖票:1
卖票:0
卖票:-1

出现了两个不该出现的结果,那我们来分析一下出现这个结果的原因。
我们分三个售票点共同买10张票,当票数只剩一张的时候,假如被A拿走了,然后进入sleep中,而在这个时候,B也将最后一张票拿走了,而此时的tickets还等于1,当A把tickets放回的时候,tickets--变为了0,当B再放回的时候则出现了-1的情况。这就是不用步造成的。
那我们就要解决这个不同步的问题
下面介绍一下synchronized关键字:
    当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
    当两个并行的线程同时访问object的一个synchronized修饰的同步块是,同一时间只能有一个线程可以对其访问,只有当他访问完之后,另一个才能对其访问。
建立一个同步方法:

class Demo implements Runnable {
	private int ticket = 10;

	public synchronized void method(){
		while (this.ticket > 0) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+" 卖票:" + this.ticket--);
		}
	}
	public void run() {
		method();
	}
};

public class Test1 {
	public static void main(String args[]) {
		// 准备四个售票点
		Demo d = new Demo();

		Thread t1 = new Thread(d,"a");
		Thread t2 = new Thread(d,"b");
		Thread t3 = new Thread(d,"c");
		
		t1.start();
		t2.start();
		t3.start();
	}
};

 运行结果:
b 卖票:10
b 卖票:9
b 卖票:8
b 卖票:7
b 卖票:6
b 卖票:5
b 卖票:4
b 卖票:3
b 卖票:2
b 卖票:1
这个结果虽然对票数同步了,但似乎又出现了其他的问题,一个售票点就把所有的票卖完了,回头看看我们的代码是哪里出了问题。
做如下改正:

class Demo implements Runnable {
	private int ticket = 10;

	public synchronized void method() {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " 卖票:"
					+ this.ticket--);
	}

	public void run() {
		while (this.ticket > 0) {
			method();
		}
	}
};

 运行结果:
a 卖票:10
a 卖票:9
a 卖票:8
a 卖票:7
a 卖票:6
a 卖票:5
c 卖票:4
c 卖票:3
b 卖票:2
b 卖票:1
c 卖票:0
a 卖票:-1
又出问题了(无奈),看看又是什么情况,我们在同步方法中加一个if(this.tickets>0)保护,在运行,看看结果吧... ...终于没问题了。

显示全文