java 多线程2 线程池和线程安全


在这里插入图片描述

  实现 Callable 的方法创建线程,不常用。


public class MyCallable implements Callable {

	// 任务方法 
	@Override
	public Object call() throws Exception {
		for (int i = 1; i <=10; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
		return null;
	}
}

public class Test {

	public static void main(String[] args) {
		//创建任务对象的前身
		MyCallable mc1=new MyCallable();
		MyCallable mc2=new MyCallable();
		
		//创建任务对象
		FutureTask ft1=new FutureTask(mc1);
		FutureTask ft2=new FutureTask(mc2);
		
		//创建线程对象
		Thread t1=new Thread(ft1);
		Thread t2=new Thread(ft2);
		
		//启动线程
		t1.start();
		t2.start();

	}

}

  线程池是线程容器,可设定线程分配的数量上限。将预先创建的线程对象存入池中,并重用线程池中的线程对象,避免频繁的创建和销毁。
  线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。

public class Test {

	public static void main(String[] args) {
		//创建单个线程池,一个线程走完才会走另一个
		//ExecutorService pool1=Executors.newSingleThreadExecutor();
		
		// 获取固定数量的线程池。
		//ExecutorService pool1=Executors.newFixedThreadPool(2);
		
		//获得动态数量的线程池,如不够则创建新的,无上限。
		ExecutorService pool1=Executors.newCachedThreadPool();
		
		//从线程池中取出线程执行任务
		
		pool1.submit(new Runnable() {
			
			@Override
			public void run() {
				for (int i = 1; i <=10; i++) {
					System.out.println(Thread.currentThread().getName()+":"+i);
				}
				
			}
		});
		
		pool1.submit(new Runnable() {
			
			@Override
			public void run() {
				for (int i = 1; i <=10; i++) {
					System.out.println(Thread.currentThread().getName()+":"+i);
				}
				
			}
		});

		//关闭线程池
		pool1.shutdown();
	}
}

  线程安全问题。
  当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。临界资源是共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。原子操作是不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。

  经典卖票问题了解线程同步锁。
  同步代码块,将要一起执行的代码绑定在一起,如果一个线程进入这个代码块,它会自动上锁,不让其他的线程进入这个代码块中去执行,直到这个线程执行完这个代码块,自动解锁了,其他的线程才能进入执行。

public class MyRun implements Runnable {

	public int ticket = 1000;
	
	public Object lock = new Object();
	
	public MyRun() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		
		while (true) {
			// 同步代码块内一次只能进入一个线程
			synchronized (lock) {
				if (ticket > 0) {
					System.out.println(Thread.currentThread() + "  " + ticket);
					ticket --;
				} else {
					System.out.println(Thread.currentThread() + " 票卖完了");
					break;
				}
			}
		}
	}
}

public class test {	

	public static void main(String[] args) {
		
		MyRun r1 = new MyRun();
		
		for (int i = 0; i < 5; i++) {
			Thread t1 = new Thread(r1);
			t1.start();
		}
	}
}

  同步方法。锁范围约小越好。


public class MyRun implements Runnable {

	public int ticket = 1000;
	
	public Object lock = new Object();
	
	public MyRun() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {		
		while (true) {
			if (saleTicket() == false) {
				break;
			}
		}
	}
	
	public synchronized boolean saleTicket() {
		if (ticket > 0) {
			System.out.println(Thread.currentThread().getName() + " " + ticket);
			ticket --;
			return true;
		} else {
			System.out.println(Thread.currentThread().getName() + " 票已卖完");
			return false;
		}
	}
}

  重入锁(对象互斥锁)。


public class LockRunnable implements Runnable {

	public int ticket = 1000;
	
	// 创建锁对象
	Lock lock = new ReentrantLock();
	
	public LockRunnable() {
		
	}

	@Override
	public void run() {
		try {
			// 锁住,只允许一个线程进入
			lock.lock();
			
			while (true) {
				if (ticket > 0) {
					System.out.println(Thread.currentThread().getName()+ " " + ticket);
					ticket --;
				} else {
					System.out.println(Thread.currentThread().getName() + " 票卖完了");
					break;
				}
			}
			
		} finally {
			// 解锁,重新抢 cpu 时间片
			lock.unlock();
		}	
	}
}

public class test {

	public test() {
		
	}

	public static void main(String[] args) {
		
		LockRunnable r1 = new LockRunnable();
		
		for (int i = 0; i < 5; i++) {
			Thread t1 = new Thread(r1);
			t1.start();
		}	
	}
}

  重入锁最好和 try{} finally{} 异常块一起使用,防止代码块执行完,却没有正确释放锁标记,造成死锁。
  一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值