Java高并发程序设计(二)——JAVA并行程序基础(二)

volatile与Java内存模型

Java的内存模型都是围绕着原子性,可见性,有序性展开的。为了确保这三个特性,Java使用了一些关键字来告诉虚拟机这些特殊的操作需要特别注意。volatile就是其中之一。
当你使用volatile关键字来声明一个变量时,虚拟机会告诉编译器,这个变量极不稳定,很容易改变,需要特别注意。我们通过一个示例来看下:

package JAVA线程方法解析;
/**
 * Author select you from me
 * func  测试volatile关键字
 */
public class VolatileUse {
	public  static long t = 0;
	public static class ChangeT implements Runnable{
		private long to;
		public ChangeT(long to){
			this.to=to;
		}
		@Override
		public void run() {
			while(true){
				VolatileUse.t=to;
				Thread.yield();
			}
		}
	}

	public static class ReadT implements Runnable{
		@Override
		public void run() {
			while(true){
				long tmp = VolatileUse.t;
				if(tmp!=111L&&tmp!=-999L&&tmp!=333L&&tmp!=-444L){
					System.out.println(tmp);
				}
				Thread.yield();

			}
		}
	}
	public static void main(String[] args){
		new Thread(new ChangeT(111L)).start();
		new Thread(new ChangeT(-999L)).start();
		new Thread(new ChangeT(333L)).start();
		new Thread(new ChangeT(-444L)).start();
		new Thread(new ReadT()).start();
	}
}

上边代码如果在32位JAVA虚拟机上运行,将产生下边的输出:

-4294967185
4294966297
4294966852

原因是因为long类型是64位,在32位虚拟机上操作可能产生串位的情况,那么怎么做呢?

public volatile static long t = 0; //将变量声明为volatile能保证操作的原子性。

但是volatile不能替代锁,无法保证复合操作的原子性。比如下面的一个例子:

package JAVA线程方法解析;
/**
 * Author select you from me
 * func  测试volatile关键字
 */
public class VolatileUseTwo{
	static volatile  int i=0;
	public static class Plustask implements Runnable{
		@Override
		public void run() {
			for(int k=0;k<1000;k++){
				i++;
			}
		}
	}
	public static void main(String[] args) throws  InterruptedException{
		Thread[] threads = new Thread[10];
		for(int i =0;i<10;i++){
			threads[i]=new Thread(new Plustask());
			threads[i].start();
			//threads[i].join();//如果把这个join有序去掉,那么这个i值会一直小于10000
		}
		System.out.print(i);
	}
}
  • 线程组
    在一个系统中,如果线程很多,而且功能分配比较明确,建议将相同功能的线程放到线程组里。下面来看一下线程组的使用:
package JAVA线程方法解析;

/**
 * Author select you from me
 * func  线程组的使用
 */
public class ThreadGroupUse implements Runnable{

	public static void main(String[]args){
		ThreadGroup threadGroup = new ThreadGroup("PrintGroup");//定义线程组的名字
		Thread thread1 = new Thread(threadGroup,new ThreadGroupUse(),"thread1");
		Thread thread2 = new Thread(threadGroup,new ThreadGroupUse(),"thread2");
		thread1.start();
		thread2.start();
		System.out.println(threadGroup.activeCount());//获得线程的总数,不确定的。
		threadGroup.list();//线程组中所有的线程信息
	}
	@Override
	public void run() {
		String goupAndName = Thread.currentThread().getThreadGroup().getName()
				+"--"+Thread.currentThread().getName();
		while(true) {
			System.out.println("I am" + goupAndName);
			try {
				Thread.sleep(3000);
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
  • 守护线程(Daemon)
    守护线程是一种特殊的线程,它是系统的守护者,比如垃圾回收线程,JIT线程就可以理解为守护线程,而我们建立的线程被称为用户线程,当系统中只有守护线程时,虚拟机会自然退出,看下面的例子:
package JAVA线程方法解析;


/**
 * Author select you from me
 * func  守护线程
 */
public class DaemonUse {
	public static class DaemonT extends  Thread{
		@Override
		public void run() {
			while(true){
				System.out.println("I am alive");
				try{
					Thread.sleep(1000);
				}catch (InterruptedException e){
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) throws InterruptedException{
		Thread t = new DaemonT();
		t.setDaemon(true);
		//设置守护线程,必须在start()方法之前,否则会设置失败。并被认为是用户线程一直向下执行。
		t.start();
		Thread.sleep(1000);
	}
}

  • 线程优先级
    线程可以有自己的优先级,优先级高的线程在竞争资源的时候更有优势。但也只是大概率问题。
    Java中使用1-10表示线程的优先级。数字越大优先级越高。
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;

我们简单举个例子:

Thread t1 = new HighPriority();
Thread t2 = new LowPriority();
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();

一般情况下,线程1会比线程2先执行完。

  • 线程同步
    关键字synchronized的作用是实现线程的同步,他的工作是对同步的代码加锁,使得每一次只有一个线程进入同步块,从而保证线程的安全性。
    synchronized有多种用法:
    (1)指定对象加锁:对给定对象加锁,进入同步代码前要获得指定对象的锁。
    (2)直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
    (3)直接作用于静态方法:相当于对当前类进行加锁,进入同步代码前要获得当前类的锁。
    我们举一个指定对象加锁的正确例子和反例,大家理解下什么叫指定对象加锁。
package JAVA线程方法解析;


/**
 * Author select you from me
 * func  线程安全 错误示范一:新建了两个线程,每个线程是不同的实例方法,所以线程不安全
 */
public class SynchronizedUse implements Runnable {
	static int i=0;
	public  synchronized void increase(){
		i++;
	}

	@Override
	public void run() {
		for(int j=0;j<10000;j++){
			increase();
		}
	}

	public static  void main(String []args) throws InterruptedException{
		Thread t1 = new Thread(new SynchronizedUse());
		Thread t2 = new Thread(new SynchronizedUse());
		t1.start();
		//t1.join();
		t2.start();
		//t2.join();
		Thread.sleep(2000);
		System.out.println(i);
	}
}

正确的修改方式:

package JAVA线程方法解析;


/**
 * Author select you from me
 * func  线程安全 第一种修改方式
 */
public class SynchronizedUseCrect1 implements Runnable {
	static int i=0;
	public static synchronized void increase(){
		i++;
	}

	@Override
	public void run() {
		for(int j=0;j<10000;j++){
			increase();
		}
	}

	public static  void main(String []args) throws InterruptedException{
		Thread t1 = new Thread(new SynchronizedUseCrect1());
		Thread t2 = new Thread(new SynchronizedUseCrect1());
		t1.start();
		//t1.join();
		t2.start();
		//t2.join();
		Thread.sleep(2000);
		System.out.println(i);
	}
}

package JAVA线程方法解析;


/**
 * Author select you from me
 * func  线程安全 第二种修改方式
 */
public class SynchronizedUseCrect2 implements Runnable {
	static SynchronizedUseCrect2 synchronizedUseCrect2 = new SynchronizedUseCrect2();
	static int i=0;
	public  synchronized void increase(){
		i++;
	}

	@Override
	public void run() {
		for(int j=0;j<10000;j++){
			increase();
		}
	}

	public static  void main(String []args) throws InterruptedException{
		Thread t1 = new Thread(synchronizedUseCrect2);
		Thread t2 = new Thread(synchronizedUseCrect2);
		t1.start();
		//t1.join();
		t2.start();
		//t2.join();
		Thread.sleep(2000);
		System.out.println(i);
	}
}

  • 使用Vector()代替ArrayList 使用CurrentHashMap()代替HashMap()

第二章结束。

作者:select you from me
链接:https://mp.csdn.net/mdeditor/96293004
来源:CSDN
转载请联系作者获得授权并注明出处。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值