跟着实例学习java多线程1-为什么使用?

分类: 转载文章 0人评论 1年前发布

       如今的互联网时代、移动互联网时代,我们开发的所有的系统和网站以及服务都要求支持高负载、大并发,要求的是效率,串行的代码已经不能满足我们对执行效率的需求,当然也会白瞎了如今这么好的硬件,现在是个台式机至少都是2核、4核的了,更别说32核、64核的高端服务器,要想充分利用cpu资源那就不能让他们闲着,所以多线程并发也就越来越重要了。

线程有很多优势:

1、提高多处理器的利用效率

2、简化业务功能设计

3、实现异步处理

多线程的风险:

1、共享数据的线程安全性

2、多线程执行时的活跃性问题

3、多线程的所带来的性能损失问题

线程安全

线程的优势我们很清楚,线程的风险我们也都知道,但是要做好风险控制就没有那么简单了。

我们先来用实例看一下共享数据的线程安全如何实现。

       对象是否需要做线程安全处理主要是看对象中是否有可变状态的变量,如果有,那么在多线程并发情况下就必须做线程安全处理,线程安全处理就是将可变状态的变量做同步处理。

package com.home.thread;
/**
 * @author gaoxu
 * 
 */
public class SafeThread {
 int id = 0;
 @Unsafe
 public int getId(){
  return ++id;
 }
}

这个类就是一个非线程安全类,因为其中的id是一个可变状态的变量,如果单线程这就是一个id线性递增的变量,每次都可以取出比上一次大的数值,但是如果并发多线程访问,这个变量的原子性就不能保证了,这时会出现某几个线程拿出相同id值的现象,这种现象就是竞态条件。

原子性,我们来说一下原子性,就这个例子来说,我们先看一下id这个变量的结果是如何计算出的:读取id的值,将值加1,然后将计算结果写回id,这是一个“读取-修改-写入”的操作序列,这样一个操作序列如果是在每一个时刻都只有一个线程对这个变量进行这样一系列的操作,那么就不会出现竞态条件了,但是实际情况可能是在没有做线程安全处理的情况下某几个线程都同时读取了这个id的值,然后完成了修改和写入,这时这几个线程所获得的值就是相同的。

让我们来做一个试验

package com.home.thread;
/**
 * @author gaoxu
 * 
 */
public class ThreadStart {
 public static void main(String[] para){
  SafeThread safe = new SafeThread();
  for(int i=0;i<10;i++){
   ThreadRead1 t1 = new ThreadRead1(safe);
   ThreadRead2 t2 = new ThreadRead2(safe);
   t1.start();
   t2.start();
  } 
 }
}


我们在这里启动二十个线程,每个线程的执行如下
package com.home.thread;
/**
 * @author gaoxu
 *
 */
public class ThreadRead1 extends Thread{
	SafeThread safe = null;
	public ThreadRead1(SafeThread o){
		safe = o;
	}
	public void run()
	{
		for(int i=0;i<10;i++)
			System.err.println(ThreadRead1.class+"--单线程id="+safe.getId());
	}
}
package com.home.thread;
/**
 * @author gaoxu
 *
 */
public class ThreadRead2 extends Thread {
	SafeThread safe = null;
	public ThreadRead2(SafeThread o){
		safe = o;
	}
	public void run()
	{
		for(int i=0;i<10;i++)
			System.out.println(ThreadRead2.class+"--单线程id="+safe.getId());
	}
}


运行结果如下,我们可以清楚的看到两个线程获得了同样的结果。(当然这样的现象不是每次都出现,但是一旦出现那将是致命的)

那么我们该怎么样修改,保证它的线程安全呢?

同步处理,将操作可变状态的变量做同步处理,也就是互斥处理,代码如下:

package com.home.thread;
/**
 * @author gaoxu
 * 
 */
public class SafeThread {
	int id = 0;
	@safe
	public synchronized int getId(){
		return ++id;
	}
}

我们使用了synchronized加锁,这样处理后操作id的可变状态就变成了原子的了,这样无论多少个线程访问,他们都不会得到相同的数值。

今天实践学习多线程就到这里,下面我们来思考几个问题,这些问题我们会在后面的文章中逐个回答。

1:我们可以看到上面例子的synchronized是加在了方法上,那么我们还可以怎么写呢?

2:synchronized锁住的是对象还是代码或方法?

上一篇:
下一篇:
0 条评论