进程
进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有他自己的内存空间和系统资源
多进程意义在于计算机可以执行多个任务,提高cpu使用率
我们在一边玩游戏,一边听音乐的时候,是cpu在做着程序间的高效切换让我们觉得是同时进行的
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。 如果是模拟出来的多线程,即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉
线程
线程是依赖于进程而存在的,在一个进程内又可以执行多个任务,而这每个任务我就可以看出是一个线程
线程:是程序的执行单元,执行路径。是程序使用cpu的最基本单位。
单线程:程序只有一条执行路径
多线程:程序有多条执行路径
多线程意义在于提高应用程序的使用率。不是提高程序的执行速度
程序的执行其实都是在抢cpu的资源,cpu的执行权
多个进程是抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到cpu的执行权,所以线程的执行有随机性
注意:
并行和并发的区别:
前者:逻辑上同时发生,指在某一个时间段同时运行多个程序
CPU多核,多个线程可以同时执行线程池
后者:物理上同时发生,指在某一个时间点同时运行多个程序
CPU单核,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉
并发编程本质就是充分利用CPU的资源
java程序的运行原理
由java命令启动jvm,jvm启动就相当于启动了一个进程,接着有这个该进程创建了一个主线程去调用main()方法
那么jvm虚拟机的启动是多线程,因为垃圾回收线程也要先启动,否则很容易出现内存溢出。主线程加垃圾回收线程
小结:
线程就是独立的执行路径;
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
main()成为主线程,为系统的入口,用于执行整个程序;
在一个进程中,如果开辟了多个线程,线程的运行由调度器(CPU)安排调度,调度器是与操作系统紧密相关,先后顺序是不能人为的干预的;
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
线程会带来额外的开销,如CPU调度时间,并发控制开销;
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;
如何实现多线程的程序?
继承Thread类:
子类继承Thread类具备多线程能力
启动线程:子类对象.start()方法
不建议使用:避免OOP单继承局限性
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ThrendDemo extends Thread{
//src="http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20191101021614.png"
//src="http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20201015024451.png"
//src="http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20190902094518.png"
String url;
String name;
public ThrendDemo(String url,String name){
this.url=url;
this.name=name;
}
@Override
public void run() {
DownWeb downWeb = new DownWeb();
downWeb.downLoader(url,name);
System.out.println("下载了文件名为"+name);
}
public static void main(String[] args) {
ThrendDemo th1 = new ThrendDemo("http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20191101021614.png","1.jpg");
ThrendDemo th2 = new ThrendDemo("http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20201015024451.png","2.jpg");
ThrendDemo th3 = new ThrendDemo("http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20190902094518.png","3.jpg");
th1.start();
th2.start();
th3.start();
}
}
class DownWeb{
public void downLoader(String url,String name) {
try{
FileUtils.copyURLToFile(new URL(url),new File(name));
}catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,下载失败");
}
}
}
另一种方法来创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动
实现接口方式的好处
可以避免由于java单继承带来的局限性;适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想
为什么要重写run()方法?
run()里面封装的是被线程执行的代码
启动线程对象用的是哪个方法?
start()
run()和start()方法的区别?
run()直接调用的仅仅是普通方法
start()先启动线程,再由jvm调用run()
实现接口Runnable具有多线程能力
启动线程:new Thread(传入目标对象).start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ThrendDemo implements Runnable{
String url;
String name;
public ThrendDemo(String url,String name){
this.url=url;
this.name=name;
}
@Override
public void run() {
DownWeb downWeb = new DownWeb();
downWeb.downLoader(url,name);
System.out.println("下载了文件名为"+name);
}
public static void main(String[] args) {
ThrendDemo th1 = new ThrendDemo("http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20200528052009.png","4.jpg");
ThrendDemo th2 = new ThrendDemo("http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20191105020544.png","5.jpg");
ThrendDemo th3 = new ThrendDemo("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1603600907&di=62c16889d83b7e6742012de55df48d04&src=http://meitu.qqzong.com/www.52520qq.com/uploads/allimg/181106/003G51110-0.jpg","6.jpg");
// th1.start();
// th2.start();
// th3.start();
new Thread(th1).start();
new Thread(th2).start();
new Thread(th3).start();
}
}
class DownWeb{
public void downLoader(String url,String name) {
try{
FileUtils.copyURLToFile(new URL(url),new File(name));
}catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,下载失败");
}
}
}
实现Callable接口
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务:
5.提交执行
6.获取结果
7.关闭服务
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class ThrendDemo implements Callable<Boolean> {
String url;
String name;
public ThrendDemo(String url,String name){
this.url=url;
this.name=name;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThrendDemo th1 = new ThrendDemo("http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20200528052009.png","4.jpg");
ThrendDemo th2 = new ThrendDemo("http://iflyssedata.oss-cn-shanghai.aliyuncs.com/images/20191105020544.png","5.jpg");
ThrendDemo th3 = new ThrendDemo("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1603600907&di=62c16889d83b7e6742012de55df48d04&src=http://meitu.qqzong.com/www.52520qq.com/uploads/allimg/181106/003G51110-0.jpg","6.jpg");
// 4.创建执行服务:
ExecutorService executorService =Executors.newFixedThreadPool(3);
// 5.提交执行
Future<Boolean> future1= executorService.submit(th1);
Future<Boolean> future2= executorService.submit(th2);
Future<Boolean> future3= executorService.submit(th3);
// 6.获取结果
future1.get();
future2.get();
future3.get();
// 7.关闭服务
executorService.shutdownNow();
}
@Override
public Boolean call() throws Exception {
DownWeb downWeb = new DownWeb();
downWeb.downLoader(url,name);
System.out.println("下载文件名为"+name);
return true;
}
}
class DownWeb{
public void downLoader(String url,String name) {
try{
FileUtils.copyURLToFile(new URL(url),new File(name));
}catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,下载失败");
}
}
}
匿名内部类(重写run)
Thread thread = new Thread(new Runnable() {
public void run() {
}
});
thread.start();
由于线程是依赖进程而存在,所以应该先创建一个进程出来,而进程是系统创建的,所以我们应该去调用系统功能创建一个进程。java是不能直接调用系统功能的,依赖于c/c++去调用系统功能创建进程,然后java去调用这样的东西
(Unsafe类 java无法操作内存,java可以调用c++ native c++可以操作内存)
(1)如何设置线程对象的名称?
public final String getName();获取线程的名称
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<200;i++) {
System.out.println(getName()+"点赞"+i);
}
}
Thread-0点赞0
Thread-0点赞1
Thread-0点赞2
Thread-0点赞3
Thread-0点赞4
Thread-1点赞0
(2)如何设置线程对象的名称
public final void setName(String name)
//默认无参构造+setName()来设置线程名称
// //创建两个线程对象
// MyThreadDemo myThreadDemo1 = new MyThreadDemo();
// MyThreadDemo myThreadDemo2 = new MyThreadDemo();
// //设置线程名称
myThreadDemo1.setName("线程一");
myThreadDemo2.setName("线程二");
// //线程启动
// myThreadDemo1.start();
// myThreadDemo2.start();
//通过有参构造来设置线程名称
MyThreadDemo myThreadDemo3 =new MyThreadDemo("线程三");
MyThreadDemo myThreadDemo4 = new MyThreadDemo("线程四");
myThreadDemo3.start();
myThreadDemo4.start();
线程二点赞9
线程一点赞0
线程一点赞1
线程一点赞2
线程一点赞3
针对不是Thread类的子类中来获取线程对象的名称?
public static Thread currentThread()
Thread.currentThread().getName()
(3)线程有个默认优先级就是5
如何设置优先级?(1最低优先级,10最高优先级,优先级用数字表示,范围从1~10)
java使用的是抢占式调度模型 优先让优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的cpu时间片相对多一些
线程优先级高仅仅表示线程获取的cpu时间片的几率高,但是要在运行次数多的时候才会看到比较好的效果
public final int getPriority();返回线程对象的优先级
public final void setPriority(int newPriority)//更改线程的优先级
注意:优先级的设定建议在start()调度前;优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看cpu的调度
public class ThreadPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
ThreadPriority threadPriority = new ThreadPriority();
Thread thread1 = new Thread(threadPriority);
Thread thread2 = new Thread(threadPriority);
Thread thread3 = new Thread(threadPriority);
Thread thread4= new Thread(threadPriority);
Thread thread5= new Thread(threadPriority);
Thread thread6 = new Thread(threadPriority);
thread1.setPriority(8);
thread1.start();