进程:
用jps 、ps-ef能看见的,就是进程。
程序启动之后,这个程序就是进程;但是有些程序会启动多个进程(像nginx)。
Jvm会为进程在内存中分配一个独立的运行空间
线程:
程序内部的一个独立的空间;在进程内部,再划分一些独立的空间。线程有自己的内存空间,就是有栈(存储局部变量,操作数)。线程在工作的时候本质就是调一些方法,我这个线程和另一个线程调用同一份代码,方法的代码只有一份,线程有两个实例在工作,这两个线程中的局部变量是不相同,在内存里面划分两片独立的空间,各自存自己的局部变量,但是两个线程运行的逻辑是同一份代码。QQ同时跟很多人聊天,就是很多的线程。图形化界面的程序,线程很多,类似word之类的。
传统Web服务,来一个用户请求,就开一个线程去响应他。线程的事情一般丢给Tomcat去做。
多线程:
就是在一个进程里面开启多个线程,让多个线程同时去完成某些任务。比如后台服务系统,就可以用多个线程同时响应多个客户请求。
为什么多个线程可以同时运行:
其实根本不可能同时运行,最多有cpu个同时运行,只是cpu切换很快而已。Cpu在线程之间切换。
Java实现线程的两种方法:
继承Thread;实现runnable()方法
public class MyThreadExtends extends Thread{
String flag;
public MyThreadExtends(String flag){
this.flag = flag;
}
public void run(){
String tname = Thread.currentThread().getName();
System.out.println(tname + "线程run方法被调用了");
Random random = new Random();
for(int i = 0; i<20; i++){
try {
Thread.sleep(random.nextInt(10)*100);//sleep里面是毫秒
System.out.println(tname+ "...."+ flag);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread thread1 = new MyThreadExtends("a");
Thread thread2 = new MyThreadExtends("b");
// thread1.start();
// thread2.start();
// thread1.run();
// thread2.run();
//两种的区别,
//run()这个是简单的调用,工作实在主线程中,主线程就
//在一个独立空间,变量什么的都在一个空间中。
//而start(),开启新的空间,在这个新的空间有自己的局部变量的一些
//存储的地方,有自己的程序计数器,独立的去运行。
}
}
public class MyThreadImpl implements Runnable{
int x;
public MyThreadImpl(int x){
this.x = x;
}
public void run() {
String tname = Thread.currentThread().getName();
System.out.println(tname + "线程run方法被调用了");
Random random = new Random();
for(int i = 0; i<20; i++){
try {
Thread.sleep(random.nextInt(10)*100);//sleep里面是毫秒
System.out.println(tname+ "...."+ x);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(new MyThreadImpl(1),"thread1");//new Thread 然后传一个runnable的实现
Thread thread2 = new Thread(new MyThreadImpl(2),"thread2");
// thread1.start();
// thread2.start();
// thread1.run();
// thread2.run();
}
}
同步锁synchronized:
(java的关键字)同样的代码块,不通的线程,只能有一个线程执行。解决共享数据冲突
伪代码:
synchronized( 需要任意一个对象(锁) )
{
代码块中放操作共享数据的代码;
}
缺陷:
(1)同步代码块里面,发生异常,jvm会释放这个锁,让其他线程享有。
(2)获取锁的线程执行完代码块,然后才释放对锁的占有
(3)降低效率,会等待很长时间
Lock:
(是类)可以不让等待的线程一直无期限的等待下去。比较:Synchronized 不需要用户手动释放锁,当 synchronized代码块执行完后,系统会自动释放对锁的占用。 Lock需要用户去手动释放锁。lock接口中的方法:
Public interface Lock{
void lock();
void lockInterruptibly() throw InterruptedException;//等待的人可以中断,就是说可以不用等了,可以去做其他事情了
boolean trylock();//尝试获取锁,可以不用等待,可以做一些异步的功能
boolean trylock(long Time,TimeUnit unit) throw InterruptedException;//尝试过程可以设置等待时间
void unlock();
}
读写锁(ReadWriteLock):
读操作是可以并行读的;然而写操作才可能产生冲突,我写的时候,你就不能读了,你也就更不能写了。如果大家都拿到的是读的锁,那大家可以同时进行都操作,但是如果其中有一个人拿到了写操作的时候,其他人就不能读也不能写。
ReadWriteLock接口中的方法:
Public interface ReadWriteLock{
Lock readLock();
Lock writeLock();
}
Lock和synchronized选择:
1、Lock是接口,而synchronized是关键字,synchronized是内置的语言实现
2、Synchronized在发生异常的时候,会自动释放锁,所以不会产生死锁的现象。而Lock发生异常的时候,如果没有主动unlock释放锁,就会造成死锁现象,因此使用lock的时候,一定要在finally里面进行unlock。
3、Lock可以让等待的线程中断,而synchronized却不行,使用synchronized时,等待线程会一直等待下去,不能响应中断。
4、通过Lock可以知道有没有获取锁,但是synchronized不行。
5、Lock可以提高多线程进行读操作效率。
在性能上来说,如果竞争不激烈的时候,两则性能差不多,但是竞争激烈的时候,此时lock优于synchronized。
上述总结:
实际生产环境中,不会new Thread().start(); 来一个客户端就产生一个线程,当请求量大的时候,超过了系统内存承受能力时候,并且线程多了, cpu 需要在线程之间做轮询,也损耗资源,影响机器的性能和稳定性。所以就有了线程池。
线程池:
一定线程数量,根据需要改变run方法进行不通逻辑的实现。线程池可以做成固定大小的,也可以做成弹性伸缩的。线程池实质就是一个数组或者时Array或者LinkList,把线程事先new好放进去。
线程池的经验之谈:
线程池在单机版的时候有用,在分布式的时候就不管用了,比如:抢购系统,需要高的响应速度,但是操作数据库库存又是很慢的操作,所以当用户请求的时候,会等很久得不到反应。所以此时可以这么做,把用户的请求丢到队列里面,相当于放到内存里面,然后返回一个等待页面给用户,这里实现了异步解耦,用户收到等待消息,然后服务器就自己去从队列里面取出请求,然后进行处理。这样看起来好像可以,但是一个公司抢购的业务往往不止一个,可能有10多个,20多个,甚至更多,并且都是几千万几千万的并发,那就会导致内存爆炸,这个时候就需要开启另外一台服务器,专门做消息队列的(JMS)。
线程池在java.util.concurrent包里面。
在这个包里面,有5中线程池创建方式:
1、Single Thread Executor:只有一个线程的线程池
代码:Executors.newSingleThreadExecutor()
2、Cached Thread Pool:线程池里面有很多线程看同时执行的,老的线程会被新的任务触发重新执行,如果有一个线程超过60秒没有任务执行,那么将终止并从线程池中删除。
代码:Executors.newCacheThreadPool()
3、Fixed Thread Pool:固定数量的线程池,如果没有新任务,那么线程会一直等
代码:Executors.newFixedThreadPool(int size)
线程数量根cpu保持一致,跟任务类型有关(io密集型还是运算密集型),运算密集型,就设小一点。io密集型设置大一点,因为里面涉及到硬件底层的中断,阻塞,通知,cpu相对快,所以开多点线程,但是硬盘速度也是有限的,开再多的线程速度也是有限的,所以这时相对的。
Cpu数量获得: int CpuNums = RunTime.getRunTime().availableProcessors();
4、Scheduled Thread Pool:就是有世家规定,例如:把任务丢进去,我不让你马上执行,隔五分钟在执行或者每两分钟执行一次。
代码:Executors.newScheduledThreadPool()
5、Single Thread Scheduled Pool:只有一个线程
代码:Executors.newSingleThreadScheduledExecutor()
synchronized中notify和wait的一些知识:
Public void run(){
Synchronized(需要被锁的对象obj){
Obj.notify();//唤醒其他也在等待obj资源的线程,把obj交给这个线程,但是本线程并没有阻塞,而是继续执行
Obj.wait();//该线程释放cpu控制,释放obj的锁,该线程阻塞,等待被唤醒,那么此时另一个线程获得锁,继续执行
}
}