【Java高并发学习】线程相关(二)

线程相关

1.线程组

如果线程数量过多,而且功能分配明确,我们可以把功能一样的线程放入一个线程组中统一进行操作。

/**
 * 线程组:将类似功能的线程放入同一组内,便于管理
 * @author wsz
 * @date 2017年11月27日
 */
class Print1 implements Runnable{

	@Override
	public void run() {
		String name = Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName();
		while(true) {
			System.out.println(name);
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
class Print2 implements Runnable{

	@Override
	public void run() {
		String name = Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName();
		while(true) {
			System.out.println(name);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
public class ThreadGroupTest implements Runnable{

	public static void main(String[] args) {
		ThreadGroup group = new ThreadGroup("ThreadGroup");//线程组及命名
//		Thread tg1 = new Thread(group,new ThreadGroupTest(),"tg1");
//		Thread tg2 = new Thread(group,new ThreadGroupTest(),"tg2");
		Thread tg1 = new Thread(group, new Print1(), "print1");
		Thread tg2 = new Thread(group, new Print2(), "print2");
		tg1.start();
		tg2.start();
		System.out.println(group.activeCount());//估值活动线程数目
		group.list();//线程组中线程信息
	}

	@Override
	public void run() {
		String name = Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName();
		while(true) {
			System.out.println(name);
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

2.守护线程

守护线程一般用于系统性的服务,比如垃圾回收线程、JIT线程。当系统中的用户线程全部结束工作后,守护线程也将结束工作。
注意:守护线程的设置必须在开启之前,否则将出现异常。

/**
 * 守护线程:一般在后台完成系统性的服务,比如垃圾回收线程、JIT线程。
 * 工作线程:完成程序业务的线程。当期线程结束,那么守护线程也将停止工作。因此当程序中只存在守护线程时,java虚拟机就会退出。
 * @author wsz
 * @date 2017年11月27日
 */
class Daemon1 implements Runnable{

	@Override
	public void run() {
		while(true) {
			System.out.println("alive");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
public class DaemonThread {

	public static void main(String[] args) throws InterruptedException {
		Thread t = new Thread(new Daemon1());
		//设置为守护线程,且必须在线程start之前设置,否则出现java.lang.IllegalThreadStateException异常,
		//且线程将一直打印“alive”,因为当前程序只有main主线程,当main休眠2秒后程序将退出。
		//如果t不是守护线程,在main结束后,t线程仍将一直打印不会结束。
		t.setDaemon(true);
		t.start();
//		t.setDaemon(true);  //出现异常,且不会终止,将一直打印。
		t.sleep(2000);
	}
}

3.线程优先级

优先级别高的线程有更高的机会获取足够的资源运行。使用1-10表示线程优先级,数字越大线程优先级越高。Thread类中定义了三个静态标量。

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

/**
 * 线程优先级:数字1-10,数字越大级别越高
 * @author wsz
 * @date 2017年11月28日
 */
public class PriorityThread {
	public final static Object object = new Object();
	
	class HighThread implements Runnable{
		int count = 0;
		@Override
		public void run() {
			while(true) {
				synchronized (object) {
					count++;
					if(count > 200000) {
						System.out.println("HighThread is ok");
						break;
					}
				}
			}
		}
		
	}
	class LowThread implements Runnable{
		int count = 0;
		@Override
		public void run() {
			while(true) {
				synchronized (object) {
					count++;
					if(count > 200000) {
						System.out.println("LowThread is ok");
						break;
					}
				}
			}
		}
		
	}
	
	public static void main(String[] args) {
		Thread high = new Thread(new PriorityThread().new HighThread());
		Thread low = new Thread(new PriorityThread().new LowThread());
		high.setPriority(Thread.MAX_PRIORITY);
		low.setPriority(Thread.MIN_PRIORITY);
		high.start();
		low.start();
	}

}

4.synchronized

程序并行是为了获取更高的执行效率,但必须得以准确性为前提。
volatile关键字并不能真正保证线程安全。只能保证一个线程修改数据后,其他线程能够看到这次改动;但当多个线程同时修改某一个数据时,依旧会发生冲突。
public class AddThread implements Runnable{
	static AddThread instance = new AddThread();
	static volatile int count = 0; 
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(count);//count值一般总是小于2个线程累加之和的。即使使用了volatile
	}

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

为了实现线程之间的同步可以使用synchronized关键字对同步的代码块进行加锁。当线程进入代码块之前需要先获得对应的锁资源,即可保证线程间的安全性,并确保线程的原子性、可见性(只能由一个线程运行同步块内的程序,不会冲突)、有序性(同步块内程序只能由一个线程运行)。
  1. 指定加锁对象:对给定对象加锁,进入同步块之前必须先获得该对象的锁资源(注意锁对象的可变性,比如Integer不可变,不能作为加锁对象)
  2. 直接作用于实例方法:相当于对当前实例加锁,进入同步块之前必须先获得当前实例的锁资源
  3. 直接作用于静态方法:相当于对当前类加锁,进入同步块之前必须先获得当前类的锁资源
对刚才的代码指定加锁对象,即可保证count的值为20000。
public class AddThread implements Runnable{
	static AddThread instance = new AddThread();//同一个对象
	static volatile int count = 0; 
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(count);//count值计算正确为20000
	}

	@Override
	public void run() {
		synchronized (instance) {
			for(int i = 0; i < 10000 ;i ++) {
				count++;
			}
		}
	}
}
作用于实例对象:
public class AddThread implements Runnable{
	static AddThread instance = new AddThread();
	static volatile int count = 0; 
	public static void main(String[] args) throws InterruptedException {
		//此时指向同一个实例对象,保证线程关注到同一个对象锁资源上。
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(count);//count值计算正确为20000

	}

	@Override
	public void run() {
//		synchronized (instance) {
//			for(int i = 0; i < 10000 ;i ++) {
//				count++;
//			}
//		}
		for(int i = 0 ;i < 10000 ; i++) {
			increase();
		}
	}
	
	public synchronized void increase() {//作用于实例方法,
		count++;
	}
}
如果想新建不同的实例对象并保证线程安全,可以把同步方法设置为static修饰,这样就是对当前类进行加锁,而不是当前实例。
public class AddThread implements Runnable{
	static AddThread instance = new AddThread();
	static volatile int count = 0; 
	public static void main(String[] args) throws InterruptedException {
		//此时指向同一个实例对象,保证线程关注到同一个对象锁资源上。
//		Thread t1 = new Thread(instance);
//		Thread t2 = new Thread(instance);
		//此时作用于静态方法,可以实例化不同对象
		Thread t1 = new Thread(new AddThread());
		Thread t2 = new Thread(new AddThread());
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(count);//count值计算正确为20000

	}

	@Override
	public void run() {
//		synchronized (instance) {
//			for(int i = 0; i < 10000 ;i ++) {
//				count++;
//			}
//		}
		for(int i = 0 ;i < 10000 ; i++) {
			increase();
		}
	}
	
	public static synchronized void increase() {//作用于实例方法,
		count++;
	}
}

5.集合与线程安全问题

5.1ArrayList

public class ArrayListTest {

	static ArrayList<Integer> arry = new ArrayList<Integer>();
	
	class AddList implements Runnable{

		@Override
		public void run() {
			for(int i= 0; i< 10000 ; i++)
				arry.add(i);
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread a1 = new Thread(new ArrayListTest().new AddList());
		Thread a2 = new Thread(new ArrayListTest().new AddList());
		a1.start();
		a2.start();
		a1.join();
		a2.join();
		System.out.println(arry.size());//size值将出现小于20000的情况
	}

}
运行上面程序ArrayList将添加数字,可能出现情况:
  1. 正常结果20000
  2. 抛出下面的异常信息:在ArrayList扩容时,内部一致性被破坏,但是没有锁保护,另一个线程访问到了不一致的内部状态,出现越界问题。
  3. 打印结果小于正常值20000:此时保存容器大小的变量被多线程不正常访问,即读写不一致,导致赋值出现为题。
  4. 解决方法:可以使用线程安全的Vector代替。
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 10
	at java.util.ArrayList.add(ArrayList.java:459)
	at arrayList.ArrayListTest$AddList.run(ArrayListTest.java:14)
	at java.lang.Thread.run(Thread.java:748)
10002

5.2HashMap


HashMap也不是线程安全的。在并发下容易使得内部结构混乱,比如链变成环。Java8中已规避了成环问题,但仍不建议在多线程环境中使用,可使用线程安全的ConcurrentHashMap。
public class HashMapTest {
	static HashMap<String,Object> map = new HashMap<String,Object>(); 
	
	class AddHashMap implements Runnable{
		@Override
		public void run() {
			for(int i= 0; i< 100 ; i++)
				map.put(String.valueOf(i), i);
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new HashMapTest().new AddHashMap());
		Thread t2 = new Thread(new HashMapTest().new AddHashMap());
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(map.size());//size值将出现小于200的情况
	}

}

最后github代码地址:https://github.com/BeHappyWsz/Thread.git


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值