Java线程通俗讲解

Java线程介绍

通常情况下,在一个服务器上面运行的程序是很多的,可能同一时间会有多个客户
端的程序访问服务器,服务器都要对这些客户端做出响应。如果我们处理器有限,
只有一个处理器的时候,如何能够让这些任务看起来是并行的同时执行呢?这里就需要用到线程的知识。

当有超过一个以上执行空间时,看起来会像是有好几件事同时发生。但实际上,只有真正的多处理器系统才可以同时执行好几件事。使用java多线程可以让它看起来好像同时都在执行,也就是说,执行的动作可以在执行空间中快速切换,因此感觉上好像是每一个任务都在同时执行。在Java中,我们用Thread这个类来实现这一点。

下面我们通过代码一起来看一下如何新建一个线程(ThreadTest)。首先,我们需要新建一个Runnable对象。稍后我们会再定义Runnable类。而这个类会定义线程会执行什么样的任务。之后,我们会定义Thread对象,用它来去执行Runnable定义好的任务。然后,启动Thread,将Runnable对象的方法摆到新的执行空间中。

package web_server;

public class ThreadTest {

	public static void main(String[] args) {
		
		// 新建Runnable对象
		Runnable threadJob = new MyRunnable();
		
		// 将Runnable的实例传给Thread的构造函数
		Thread myThread = new Thread(threadJob);
		
		// 调用start()才会让线程开始执行,在此之前
		// 它只是Thread一个实例,并不是真的线程
		myThread.start();
		
		System.out.println("back in main");
	}

}

接下来我们再看如何实现Runnable。Runnable是一个接口,该接口只有一个方法,就是public void run()。在run中定义要执行的方法。因为Runnable是一个接口,线程的任务是可以被定义在任何实现Runnable的类上。线程只在乎传入给Thread的构造函数的参数是否为实现Runnable的类。当你把Runnable传给Thread的构造函数时,实际上就是在给Thread取得run的办法,这就等于你给Thread一项任务。

package web_server;

public class MyRunnable implements Runnable{
	
	// Runnable接口只有一个方法,就是public void run()
	public void run() {
		go();
	}
	
	public void go() {
		doMore();
	}
	
	public void doMore() {
		System.out.println("top o' the stack");
	}
}

线程的原子性

每一个线程变成可执行状态之后,它就会在可执行和不可执行两种状态中来来回回的切换,有的时候也会出现第三种状态,就是暂时不可执行状态。现在假设你有多个线程在排队等待执行,这时候哪个线程先执行,具体执行多长时间呢?这些工作都由调度器来实现。线程调度器会去决定哪个线程跑起来,而哪个线程会暂时不去执行。

但是这样又会有一个大问题,特别是对于多线程而言,可能会发生a线程执行一段时间,然后b线程再执行一段时间。如果a线程和b线程的程序方法互不影响还好,如果a线程和b线程使用的是同样的程序方法,可能会发生结果互相影响的问题。对于这样的问题,我们可以通过Synchronized来解决。Synchronized关键字代表线程需要一把钥匙来存取被同步化过程的线程。也就是说,通过synchronized同步化修饰过后的方法,会将方法中的内容执行完之后,再交给别的线程去执行。如果想要保护重要的数据,就把作用在数据上的方法给同步化。这部分程序,不可分割,应该被连续的执行。在古典物理学中,我们认为原子是不可分割的最小物理单元。因此我们说synchronized修饰过的方法具有原子性。我们下面通过一个例子一起来看一下。

package web_server;

public class TestSync implements Runnable{
	private int balance = 0;
	
	public void run() {
		for(int i = 0; i < 50; i++) {
			increment();
			System.out.println("balance is " + balance);
		}
	}
	
	public void increment() {
//	public synchronized void increment() {
		int i = balance;
		balance = i + 1;
	}

}

package web_server;

public class TestSyncTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TestSync job = new TestSync();
		Thread a = new Thread(job);
		Thread b = new Thread(job);
		a.start();
		b.start();
	}

}

可以看到,使用synchronized之后执行的效果看起来很正常,而不加之后有一些怪异的值。我们这里新建了两个线程,这两个线程执行的都是同样的方法。之所以不适用synchronized会出现数字乱的问题在于,比如说执行了i=balance之后,a线程就切换成b线程去执行了。还没有来得及累加。B线程执行一段时间之后再切换成a线程,然后a线程再继续累加,就会出现数字错乱的问题。而使用synchronized会让其强制执行完累加之后再去切换线程,所以不会出现错乱。

最后附上 本文内容对应的视频讲解

参考资料

《Head First Java》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Einstellung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值