java高并发编程(三)——线程基础知识

java线程基础知识

一、volatile关键字

  为了确保线程具有原子性,可见性,有序性的特点,java使用了一些特殊的操作和关键字来申明某些数据要特别注意。volatile就是其中之一。
  当用volatile去声明一个变量的时候,就相当于告诉虚拟机,该变量很有可能被某些线程修改,确保该线程被修改后,相关线程都能清楚这一改动。但是volatile并不能代替锁,它不能保证一些复杂操作的原子性。

public class VolatileTest {
	private volatile static int a = 0;
	
	static class ThreadA implements Runnable{
		
		public void run() {
			for(int i = 0;i<10000;i++) {
				a ++}
		}
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		Thread threads[] = new Thread[20];
		
		for(int i = 0;i<20;i++) {
			threads[i] = new Thread(new ThreadA());
			threads[i].start();
		}
		for(int i = 0;i<20;i++) {
			threads[i].join();
		}
		
		System.err.println(a);
	}
}

上面的代码输入总小于20w,如果a++是原子性的话那么输入应该等于20w。

二、线程组(THreadGroup)

  如果系统的线程很多,管理起来很复杂,就可以把线程放在线程组里,线程组相当于一个篮子,用来存放和管理线程。不同线程组的线程不能修改对方线程的数据,在一定程度上保证了数据的安全。

public class ThreadGroupTest {
	
	static class MyThread implements Runnable{
		public void run() {
			String GroupAndName = Thread.currentThread().getThreadGroup().getName()+"-"+Thread.currentThread().getName();
			System.err.println(GroupAndName);
			
		}
	}
	
	public static void main(String[] args) {
		ThreadGroup tg = new ThreadGroup("MyThreadGroup");
		
		Thread t1 = new Thread(tg,new MyThread(),"m1");
		Thread t2 = new Thread(tg,new MyThread(),"m2");
		
		t1.start();
		t2.start();
		System.err.println(tg.activeCount());			 //打印线程组中所有的线程总数(动态的,所以是估计值)
		tg.list();						//打印线程组内的所有线程名称
	}

}

三、守护线程(Daemon)

   守护线程是一种特殊线程,是系统的守护者,在后台默默完成一些系统性的服务,比如垃圾回收线程,JIT线程等可以理解为守护线程,与之相对应的是用户线程,用户线程是系统的工作线程,用户线程完成的是特定的业务操作,如果用户线程全部结束,该系统就无事可做,守护线程也就没有对应要守护的对象了。简而言之就是当系统中只有守护线程时,java虚拟机就会自动退出。下面就是一个守护线程的例子。

public class DaemonTest {
	public static class DaemonThread implements Runnable{
		private boolean count = true;
		public void run() {
			while(count) {
				System.err.println("this is a DaemonThread");
				count = true;
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new DaemonThread());
		t1.setDaemon(true);
		t1.start();
		
		Thread.sleep(2000);
	}
}

当主线程休眠两秒后退出时,系统没有其余的用户线程,因此守护线程也紧接着退出。设置一个线程为守护线程时,一定要在线程start之前设置,否则会设置失败,本该设置为守护线程的线程会被作为用户线程运行。

三、线程优先级

  线程可以设置自己的优先级,优先级高的线程在竞争cpu资源的时候有更大的优势。但设置线程优先级可能出现低优先级线程一直抢占不到资源,从而产生饥饿,所以在有严格要求的场合,还是需要自己在应用层解决线程调度问题。
  一般在java中线程的优先级由数字1-10表示,数字越大,优先级越高。下面的代码体现了线程优先级在实际运用中的作用。

public class PriorityTest {
	static class HighPriority implements Runnable{
		static int count = 0;
		public void run() {
			while(true) {
				synchronized(PriorityTest.class) {
					count++;
					if(count>1000000) {
						System.err.println("高优先级线程计算完成");
						break;
					}
				}
			}
		}
	}
	
	static class LowPriority implements Runnable{
		static int count = 0;
		public void run() {
			while(true) {
				synchronized(PriorityTest.class) {
					count++;
					if(count>1000000) {
						System.err.println("低优先级线程计算完成");
						break;
					}
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Thread t1 = new Thread(new HighPriority());
		Thread t2 = new Thread(new LowPriority());
		
		t1.setPriority(10);
		t2.setPriority(1);
		t1.start();
		t2.start();
	}
}

设置为高优先级的线程有较大的概率可以先完成计算,但并非绝对。

四、synchronized关键字

  并发程序下很容易产生线程安全问题,即多个线程同时对一个数据进行改动时,会产生冲突。因此,在一个线程对共享数据进行读写等操作时,要求其他线程不能对共享数据进行读写等操作。java中使用synchronized关键字来实现这一功能。
  synchronized关键字的作用是实现线程的同步。对同步的代码加锁,在同一时间只能有一个线程对其进行操作。synchronized可以对给定的对象加锁,进入同步代码块之前要获取该对象的锁,也可以直接用于实例方法,相当于对当前实例加锁,进入同步代码块之前要获得当前实例的锁,也可以用于静态方法,相当于对当前类加锁,进入同步代码块钱要获得当前类的锁。synchronized的理解可以回顾下之前的生产者-消费者模型。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值