我的java笔记之多线程并发
1.可以运行多个独立的任务
2.考虑这些任务关闭时可能出现的问题
3.任务可能会在共享资源发生干涉。互斥是防止这种问题的方式及就是锁
4.如果设计不合理就会出现死锁
明白什么时候使用并发,什么时候避免使用并发是关键,使用他们的主要原因是
1.要处理很多事物,他们交织在一起,应用并发能够更有效的使用计算机
2.能够更好的组织代码
3.更便于用户使用
均衡资源的经典案例是在等待输入输出时使用cpu,更好的代码组织可以在仿真中看到,是用户方的经典案例是在长时间下载的过程中监听停止按钮是否被按下
线程的一个额外好处是提供了轻量级的执行上下文切换,而不是重量级切换,因为一个给定进程内的所有线程共享所有的内存空间,轻量级的上下文切换只改变程序的执行序列和局部变量,进程切换必须改变所有的内存空间
多线程的主要缺陷有
1.等待资源的时候性能降低
2.处理线程的额外cpu花费
3.糟糕的程序导致不必要的复杂度
4.有可能产生一些病态的行为如饿死,竞争,死锁,活锁
5.不同平台导致的不一定性
6.因为多个线程可能会共享一个资源,比如一个对象的内存,而且你必须确定多个线程不会同时读取和改变这个资源,这就是线程的最大难题,明智的使用加锁机制例如synchronized
关键字。
案例1建立线程任务有两种方式 实现Runnable接口这里可以使用匿名内部类来实现或者继承Thread
package cn.itcast.javasethread;
public class LiftOff implements Runnable {
protected int countDown=10;
private static int taskCount=0;
private final int id=taskCount++;//一旦被初始化不希望被改变
public LiftOff() {//无参构造方法
}
public LiftOff(int countDown) {//带参构造方法传入时间
this.countDown=countDown;
}
public String status(){
return "#"+id+"("+( countDown>0? countDown:"LiftOff")+")";
}
@Override
public void run() {
while(countDown-->0){
System.out.println(status());
Thread.yield();//静态方法
}
}
}
package cn.itcast.javasethread;
public class MainThread {
public static void main(String[] args) {
// LiftOff launch=new LiftOff();
// launch.run();//这个是普通方法的调用不是多线程
Thread t=new Thread(new LiftOff());
t.start();
}
}
#0(9)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(LiftOff)
package cn.itcast.javasethread;
public class MoreThread {
public static void main(String[] args) {
for (int i = 0; i < 5; i++)
new Thread(new LiftOff()).start();
System.out.println("wait for liftdown");
}
}
线程池
package cn.itcast.javasethread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService exec=Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++)
exec.execute(new LiftOff());
exec.shutdown();
}
}
线程池2
package cn.itcast.javasethread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPool {
public static void main(String[] args) {
ExecutorService exec=Executors.newFixedThreadPool(5);//建立有限的线程。限制线程数量
for(int i=0;i<5;i++)
exec.execute(new LiftOff());
exec.shutdown();
}
}
package cn.itcast.javasethread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService exec = Executors.newSingleThreadExecutor();//确保任何时候都只有一个线程在运行
for (int i = 0; i < 5; i++)
exec.execute(new LiftOff());
exec.shutdown();
}
}
package cn.itcast.javasethread;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* submit方法会产生Future对象,他用Callable接口返回特定的结果的特定类型进行了参数化,可以用isdone方法
* 查看任务是否玩完成
* @author likailong
*/
public class CallableDemo {
public static void main(String[] args) {
ExecutorService exec=Executors.newCachedThreadPool();
ArrayList<Future<String>>results=new ArrayList<>();
for(int i=0;i<10;i++)
results.add(exec.submit(new TaskWithResult(i)));
for(Future<String> fs:results)
try {
System.out.println(fs.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally{
exec.shutdown();
}
}
}
休眠
package cn.itcast.javasethread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SleepTask extends LiftOff {
public void run() {
while (countDown-- > 0) {
System.out.print(status());
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
System.err.println("Interrupted");
}
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++)
exec.execute(new SleepTask());
exec.shutdown();
}
}
优先级
package cn.itcast.javasethread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimplePriorrities implements Runnable {
private int countDown = 5;
private volatile double d;
private int priority;
@Override
public String toString() {
return Thread.currentThread() + ":" + countDown;
}
public SimplePriorrities(int priority) {
this.priority = priority;
}
@Override
public void run() {
Thread.currentThread().setPriority(priority);
while(true){
for (int i = 0; i < 10000; i++) {
d+=(Math.PI+Math.E)/(double)i;
if(i%1000==0)
Thread.yield();
}
System.out.println(this);
if(--countDown==0)
return;
}
}
public static void main(String[] args) {
ExecutorService exec=Executors.newCachedThreadPool();
for(int i=0;i<5;i++ )
exec.execute(new SimplePriorrities(Thread.MIN_PRIORITY));
exec.execute(new SimplePriorrities(Thread.MAX_PRIORITY));
exec.shutdown();
}
}
join方法 强制停止当前线程加入别的线程
setDaemon()设置为保护线程,isDaemon()判断是否保护线程
共享受限资源
package cn.itcast.javasethread;
/**
* 需求 其中一个任务产生偶数 其他任务消费这些数字,这里的消费任务就是检查偶数的有效性
* @author likailong
*
*/
public abstract class IntGnenerator {
private volatile boolean canceled = false;
public abstract int next();
public void cancel() {
canceled = true;
}
public boolean isCanceled() {
return canceled;
}
}
package cn.itcast.javasethread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class EventChecker implements Runnable {
private IntGnenerator generator;
private final int id;
public EventChecker(IntGnenerator g, int i) {
generator = g;
id = i;
}
@Override
public void run() {
while (!generator.isCanceled()) {
int val = generator.next();
if (val % 2 != 0) {
System.out.println("不是事件");
generator.cancel();
}
}
}
public static void test(IntGnenerator gp, int count) {
System.out.println("press control-s to exit");
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < count; i++)
exec.execute(new EventChecker(gp, i));
exec.shutdown();
}
public static void test(IntGnenerator gp) {
test(gp, 10);
}
}
package cn.itcast.javasethread;
public class EventGenerator extends IntGnenerator {
private int curentEventValue = 0;
@Override
public int next() {
++curentEventValue;
++curentEventValue;
return curentEventValue;
}
public static void main(String[] args) {
EventChecker.test(new EventGenerator());
}
}
结果
press control-s to exit
不是事件
不是事件
解决方案 加入同步
package cn.itcast.javasethread;
public class SynchroniaedEventGnerator extends IntGnenerator {
private int curentEventValue = 0;
@Override
public synchronized int next() {
++curentEventValue;
Thread.yield();
++curentEventValue;
return curentEventValue;
}
public static void main(String[] args) {
EventChecker.test(new EventGenerator());
}
}
package cn.itcast.javasethread;
public class SynchroniaedEventGnerator extends IntGnenerator {
private int curentEventValue = 0;
@Override
public synchronized int next() {
++curentEventValue;
Thread.yield();
++curentEventValue;
return curentEventValue;
}
public static void main(String[] args) {
EventChecker.test(new EventGenerator());
}
}
package cn.itcast.javasethread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MutexEventGenerator extends IntGnenerator {
private int curentEventValue = 0;
private Lock lock=new ReentrantLock();
@Override
public int next() {
lock.lock();
try{
++curentEventValue;
Thread.yield();
++curentEventValue;
return curentEventValue;}
finally{
lock.unlock();
}
}
public static void main(String[] args) {
EventChecker.test(new MutexEventGenerator());
}
}
package cn.itcast.javasethread;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class AttemptLock {
private ReentrantLock lock = new ReentrantLock();
public void untimed() {
boolean capturred = lock.tryLock();
try {
System.out.println("trylock" + capturred);
} finally {
if (capturred)
lock.unlock();
}
}
public void timed() {
boolean capturred = false;
try {
capturred = lock.tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
System.out.println("trylock(2,TimeUnit.SECONDS)" + capturred);
} finally {
if (capturred)
lock.unlock();
}
}
public static void main(String[] args) {
final AttemptLock al = new AttemptLock();
al.untimed();
al.timed();
new Thread() {
{
setDaemon(true);
}
public void run() {
al.lock.lock();
System.out.println("acquired");
}
}.start();
Thread.yield();
al.untimed();
al.timed();
}
}
原子性与易变性
银行系统模拟
package cn.itcast.javasethread1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class BankTellerSimulation {
static final int MAX_LINE_SIZE = 50;
static final int ADJUSTMENT_PERIOD = 1000;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
CustomerLine customers = new CustomerLine(MAX_LINE_SIZE);
exec.execute(new CusomerGenerator(customers));
exec.execute(new TellerManager(exec, customers, ADJUSTMENT_PERIOD));
if (args.length > 0)
TimeUnit.SECONDS.sleep(new Integer(args[0]));
else {
System.out.println("Press 'enter'to quit");
System.in.read();
}
exec.shutdown();
}
}
package cn.itcast.javasethread1;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class CusomerGenerator implements Runnable {
private CustomerLine customers;
private static Random rand = new Random();
public CusomerGenerator(CustomerLine cq) {
customers = cq;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(rand.nextInt(300));
customers.put(new Customer(rand.nextInt(1000)));
}
} catch (InterruptedException e) {
System.out.println("CusomerGenerator Interrupted");
}
System.out.println("CusomerGenerator terminating");
}
}
package cn.itcast.javasethread1;
public class Customer {
public Customer(int tm) {
serviceTime = tm;
}
private final int serviceTime;
public int getServiceTime() {
return serviceTime;
}
@Override
public String toString() {
return "Customer [serviceTime=" + serviceTime + "]";
}
}
package cn.itcast.javasethread1;
import java.util.concurrent.ArrayBlockingQueue;
public class CustomerLine extends ArrayBlockingQueue<Customer> {
public CustomerLine(int maxLineSize) {
super(maxLineSize);
}
public String toString(){
if(this.size()==0)
return "Empty";
StringBuilder result=new StringBuilder();
for(Customer customer:this)
result.append(customer);
return result.toString();
}
}
package cn.itcast.javasethread1;
import java.util.concurrent.TimeUnit;
public class Teller implements Runnable, Comparable<Teller> {
private static int counter = 0;
private int id = counter++;
private int customersServed = 0;
private CustomerLine customers;
private boolean servingCustomerLine = true;
public Teller(CustomerLine cq) {
customers = cq;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
Customer customer = customers.take();
TimeUnit.MILLISECONDS.sleep(customer.getServiceTime());
synchronized (this) {
customersServed++;
while (!servingCustomerLine)
wait();
}
}
} catch (InterruptedException e) {
System.out.println(this + "Interrupted");
}
System.out.println(this + "terminating");
}
public synchronized void dosomethingElse() {
customersServed = 0;
servingCustomerLine = false;
}
public synchronized void serveCustomerLine() {
assert !servingCustomerLine : "alread serving" + this;
servingCustomerLine = false;
notifyAll();
}
public String toString() {
return "Teller" + id + "";
}
public synchronized String shortString() {
return "T" + id;
}
@Override
public synchronized int compareTo(Teller other) {
return customersServed < other.customersServed ? -1 :
(customersServed == other.customersServed ? 0 : 1);
}
}
package cn.itcast.javasethread1;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class TellerManager implements Runnable {
private ExecutorService exec;
private CustomerLine customers;
private PriorityQueue<Teller> workingTellers = new PriorityQueue<Teller>();
private Queue<Teller> tellerDoingOtherThing = new LinkedList<Teller>();
private int adjustmentPeriod;
private static Random rand = new Random(47);
public TellerManager(ExecutorService e, CustomerLine customers, int adjustmentPeriod) {
exec = e;
this.customers = customers;
this.adjustmentPeriod = adjustmentPeriod;
Teller teller = new Teller(customers);
exec.execute(teller);
workingTellers.add(teller);
}
public void adjustTellerNumber() {
if (customers.size() / workingTellers.size() > 2) {
if (tellerDoingOtherThing.size() > 0) {
Teller teller = tellerDoingOtherThing.remove();
teller.serveCustomerLine();
workingTellers.offer(teller);
return;
}
Teller teller = new Teller(customers);
exec.execute(teller);
workingTellers.add(teller);
}
if (workingTellers.size() > 1 && customers.size() / workingTellers.size() < 2)
reassignOneTeller();
if (customers.size() == 0)
while (workingTellers.size() > 1)
reassignOneTeller();
}
private void reassignOneTeller() {
Teller teller = workingTellers.poll();
teller.dosomethingElse();
tellerDoingOtherThing.offer(teller);
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(adjustmentPeriod);
adjustTellerNumber();
System.out.println(customers + "{");
for (Teller teller : workingTellers)
System.out.println(teller.shortString() + "");
System.out.println("}");
}
} catch (InterruptedException e) {
System.out.println(this + "Interrupted");
}
System.out.println(this + "terminating");
}
public String toString() {
return "TellerMannager";
}
}
Press 'enter'to quit
Customer [serviceTime=441]Customer [serviceTime=895]{
T0;}
Customer [serviceTime=378]Customer [serviceTime=128]Customer [serviceTime=636]Customer [serviceTime=503]Customer [serviceTime=276]Customer [serviceTime=755]Customer [serviceTime=185]{
T1;T0;}
Customer [serviceTime=636]Customer [serviceTime=503]Customer [serviceTime=276]Customer [serviceTime=755]Customer [serviceTime=185]Customer [serviceTime=46]Customer [serviceTime=398]Customer [serviceTime=782]Customer [serviceTime=739]Customer [serviceTime=63]Customer [serviceTime=32]Customer [serviceTime=9]{
T2;T0;T1;}
Customer [serviceTime=46]Customer [serviceTime=398]Customer [serviceTime=782]Customer [serviceTime=739]Customer [serviceTime=63]Customer [serviceTime=32]Customer [serviceTime=9]Customer [serviceTime=304]Customer [serviceTime=777]Customer [serviceTime=365]Customer [serviceTime=542]Customer [serviceTime=894]Customer [serviceTime=307]{
T3;T2;T1;T0;}
Customer [serviceTime=542]Customer [serviceTime=894]Customer [serviceTime=307]Customer [serviceTime=586]Customer [serviceTime=348]Customer [serviceTime=127]Customer [serviceTime=262]Customer [serviceTime=412]Customer [serviceTime=720]{
T3;T2;T1;T0;}
Customer [serviceTime=127]Customer [serviceTime=262]Customer [serviceTime=412]Customer [serviceTime=720]Customer [serviceTime=783]Customer [serviceTime=944]Customer [serviceTime=388]Customer [serviceTime=307]Customer [serviceTime=962]Customer [serviceTime=645]Customer [serviceTime=771]{
T3;T2;T1;T0;}
案例代码分析
Customer对象非常简单,只包含一个final int域的。因为这些对象从来都不变化,因此他们是只读对象,并且不需要valatile.在这之上,每个Teller任务在任何时刻都只从输入列队一出一个Customer并且在这个Customer上工作直至完成,因此Customer在任何时刻都只有一个任务访问。
CustomerLine 表示顾客在等待被Teller服务时所排成的单一的行。这是一个ArrayBlockingQuene,它具有一个toString方法,可以按照我们希望的结果打印结果。
CustomerGnetater附着在CustomerLine上,按照随机的时间间隔想这个队列中加Customer
Teller从CustomerLine中取走Customer,在任何时刻他都只能处理一个顾客,并且跟踪在这个特定班次中有他服务的Customer数量。当没有足够的顾客时,他会被告知执行doSomethingElse而当出现多个顾客时,他会告知去执行serveCustomerLine为了选择下一个出纳员,让其回到服务顾客的业务上,compareTo方法将查看出纳员服务顾客的数量,似的PriorityQueue可以自动将工作量最小的出纳员推向前台。
TellerManager使各种活动的中心,他跟踪所有出纳员以及等待服务的顾客。
1:多线程(理解)
(1)多线程:一个应用程序有多条执行路径
进程:正在执行的应用程序
线程:进程的执行单元,执行路径
单线程:一个应用程序只有一条执行路径
多线程:一个应用程序有多条执行路径
多进程的意义?
提高CPU的使用率
多线程的意义?
提高应用程序的使用率
(2)Java程序的运行原理及JVM的启动是多线程的吗?
A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。
B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。
A:继承Thread类
B:实现Runnable接口
A:线程的调度
a:分时调度
b:抢占式调度 (Java采用的是该调度方式)
B:获取和设置线程优先级
a:默认是5
b:范围是1-10
(5)线程的控制(常见方法)
A:休眠线程
B:加入线程
C:礼让线程
D:后台线程
E:终止线程(掌握)
(6)线程的生命周期(参照 线程生命周期图解.bmp)
A:新建
B:就绪
C:运行
D:阻塞
E:死亡
(7)电影院卖票程序的实现
A:继承Thread类
B:实现Runnable接口
(8)电影院卖票程序出问题
A:为了更符合真实的场景,加入了休眠100毫秒。
B:卖票问题
a:同票多次
b:负数票
(9)多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据)
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
(10)同步解决线程安全问题
A:同步代码块
synchronized(对象) {
需要被同步的代码;
}
这里的锁对象可以是任意对象。
B:同步方法
把同步加在方法上。
这里的锁对象是this
C:静态同步方法
把同步加在方法上。
这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)
线程安全的类
A:StringBuffer
B:Vector
C:Hashtable
D:如何把一个线程不安全的集合类变成一个线程安全的集合类
用Collections工具类的方法即可。
1:要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在。
2:什么是进程?
通过任务管理器我们就看到了进程的存在。
而通过观察,我们发现只有运行的程序才会出现进程。
进程:就是正在运行的程序。
进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
3:多进程有什么意义呢?
单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。
举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。
也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。
并且呢,可以提高CPU的使用率。
问题:
一边玩游戏,一边听音乐是同时进行的吗?
不是。因为单CPU在某一个时间点上只能做一件事情。
而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。
4:什么是线程呢?
在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
单线程:如果程序只有一条执行路径。
多线程:如果程序有多条执行路径。
5:多线程有什么意义呢?
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。
我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。