线程重复执行: ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); exec.scheduleAtFixedRate(线程名, 1, 1, TimeUnit.SECONDS); 字符输出流writer写完后要执行flush()方法,不然内容不存在 IO流:实时交互 Socket网络交互 多线程和socket合为一体 所有软件几乎都会有多线程,所有跟网络有关的必须要使用socket 大多数的程序只能循环运行单独的程序块,但无法同时运行不同的多个程序块 Poc、计算器、记事本等都是单线程 Java支持多线程 进程:一般是对操作系统而言的(windows、linux/Android、Unix/ios) 所有的应用程序都要运行在进程中 分类: 前台进程:有界面,用户能感知 后台进程:无界面,但是后台运行(升级、maven) 线程:在应用程序中去开辟 多线程:含义是指在单个进程中,可以同时运行不同的线程,执行不同任务 共享同一块内存空间和同一组系统资源,有可能相互影响 多线程的执行策略是抢占式策略 一般由exe、虚拟机执行 单线程:同一时间,一个进程只执行一个线程,无论这个线程多复杂 进程是由操作系统来管理的,而线程则是在一个程序中(进程中) 线程优先级为十个等级:默认为5,标准普通,值越大,优先级越高 线程一词来源于linux Java实现线程: 继承Thread类(Thread类实现了Runnable的方法) 实现Runnable做公共类,使用AtomicInteger定义变量 实现Runnable接口 Thread可用静态变量int 优先级低不代表抢不到资源,而是抢到的几率小一点 多线程执行的顺序无法保证,每次顺序不一样 接口在实现未实现的方法可以被new 并发变量AtomicInteger: 做操作时其他线程等待 get()取值,set(int)设值 getandIncrement()取得之后再加一 AtomicInteger i=new AtomicInteger(20); 并发:同一时间多个方法对同一个变量进行操作 一个进程new完后只能调用一次start(),后面会报错 线程启动用start(),如果用run()则调用的是一个普通方法 同步:为了防止多个线程同时访问同一个数据对象,对象数据造成损坏 防止同步:排队,一个一个对共享资源进行操作,而不是同时操作 Java线程同步方法:synchronized 同步方法 public synchronized void test() { } 同步代码块 private static Object obj=new Object(); public void test() { synchronized(obj){ } } 或者 public void test() { synchronized(this.class){ } } 相对而言同步方法安全性要高些 死锁:同步的线程分别占用地方需要的资源不放弃,都在等待对方放弃自己需要的同步资源,从而形成线程的死锁 死锁与死循环 死锁导致的情况就是程序不执行操作了,一直在等待(不占用cpu资源) 死循环是一直在执行 Java线程通信规定使用wait()、notify()、notifyAll() 存放线程的容器叫做线程池(有序的集合,LinkedList,方便新增和删除) 线程池中的线程处于wait()状态,执行一个线程后notify()或notifyAll() 多线程中wait()、notify()、notifyAll()方法属于Object类 Wait()与sleep()区别: Wait()线程处于等待状态,释放对象锁(放弃抢占资源),让其他线程进行运行,由notify()或notifyAll()唤醒线程 Sleep()线程处于休眠状态,等到指定时间会自动唤醒,并且在休眠中不会释放对象锁 Yield()静态方法,放弃本次执行,让其他线程先执行 isAlive()判断线程是否还存在 join()将一个线程加入子线程 每个Java程序都会有一个主线程,多线程的实现就是在主线程main()中创建新的线程 线程的状态转换: 创建:Thread myThread = new MyThreadClass( ); 就绪:调用start() 运行: 阻塞:sleep()或wait()方法时 销毁:正常结束或强制结束(stop()) Suspend()容易出现死锁,被wait()替代了,用resume()唤醒 Native关键字,本地的,虚拟机的资源 AtomicInteger的方法: Set()设置值 Get()取值 如果在方法中使用了wait()或者notify()/notifyAll()这个方法必须加synchronized关键字 CPU在同一时刻处理线程数为:cpu核数*3-1 线程池: 含义: 线程池的基本思想还是一种对象池的思想,开辟一块内存空间, 里面存放了众多(未死亡)的线程,池中线程执行调度由池管 理器来处理。当有线程任务时,从池中取一个,执行完成后线 程对象归池,这样可以避免反复创建线程对象所带来的性能开 销,节省了系统的资源 作用: 优化程序,节约系统的开销 线程池比较重要的类 ExecutorService: 真正的线程池接口。 ScheduledExecutorService: 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 ThreadPoolExecutor: ExecutorService的默认实现。 ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。 配置线程池方法: newSingleThreadExecutor 创建一个单线程的线程池 newFixedThreadPool 创建固定大小的线程池 newCachedThreadPool 创建一个可缓存的线程池 newScheduledThreadPool 创建一个大小无限的线程池 执行用execute() 关闭用shutdown() 线程控制方法: start() 线程方法,新建的线程进入Runnable状态 wait()对象方法,释放对象锁,线程进入blocked状态,等待被notify或时间到期 notify()/notifyAll() 对象方法,唤醒其他的线程 yield() 线程静态方法,放弃执行,回到Runnable状态,使其他优先级不低于此线程的线程有机会先运行 sleep() 线程静态方法,线程睡眠指定的一段时间,线程进入blocked状态 join()线程方法,调用这个方法的线程,会等待加入的子线程完成 isAlive() 判断某一个线程是否还在运行run,Run方法结束返回false setPriority()设置线程优先级 currentThread()获得当前线程 getId()取得线程Id getName()取得线程名称 setName()设置线程名称 调度策略 协同式: 线程的执行时间由线程本身控制,线程把自己的工作执行完了之后,要主动通知系统切换到另一个线程上。 坏处:线程执行时间不可控制 抢占式: 高优先级的线程抢占CPU 每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定. (线程划分为1~10等级,默认为5) Java的调度方法 同优先级线程组成先进先出队列,使用时间片策略 对高优先级,使用优先调度的抢占式策略 多线程的优势 减轻编写交互频繁、涉及面多的程序的困难(如监听网络端口)。 程序的吞吐量会得到改善(同时监听多种设备, 如网络端口、串口、并口以及其他外设)。 有多个处理器的系统,可以并发运行不同的线程(否则,任何时刻只有一个线程在运行)。 进程和线程的不同点 进程是由操作系统来管理的,而线程则是在一个程序(进程)内。 不同进程的代码、内部数据和状态都是完全独立的,而一个程序内的多线程是共享同一块内存空间和同一组系统资源,有可能互相影响。 线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。 线程池实例: ExecutorService pool=Executors.newSingleThreadExecutor(); Thread t1=new MyThread(); pool.execute(t1); pool.shutdonw(); 1: 线程的基本概念、线程的基本状态及状态之间的关系? 线程是比进程更小的单位,是进程内部独立的、有序的指令流。因此,一个进程能包括多个并发执行的线程。由于各个线程之间的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度、同步等问题需要进行特殊处理。 线程有五种状态:新生态,可运行态,运行态,阻塞态,死亡态。 1、当利用new创建线程实例对象后,他仅仅作为一个实例存在,jvm没有为其分配CPU时间片等线程运行资源,该线程处于新生态。 2、可运行态,当线程对象调用了start()方法后,该线程处于可运行态,这时线程已经获得了除cpu之外的其他系统资源,只等jvm的线程调度管理器按照线程的优先级对该线程进行调度,从而使该现成拥有能够获取cpu时间片的机会。3、运行状态,jvm选中一个可运行状态线程,使其占有cpu并转换为运行状态。运行状态的线程执行自己的run()方法中的代码,直到调用其他方法而终止,或等待某资源而阻塞或完成任务而死亡。 4、阻塞状态,处于运行状态的线程在某些情况下,如执行了sleep()方法,或等待I/O设备资源,将让出cpu并暂时终止自己的运行,进入阻塞状态,也称为不可运行状态。出于阻塞状态的线程是不可执行的,即使cpu空闲 5、死亡状态,死亡状态是线程生命周期中最后一个阶段。导致现成死亡有两种情况:一种是线程正常完成了全部工作,即run()方法的代码。另一种是线程被强制性的终止,如通过stop()方法来终止一个线程。 2:多线程有几种实现方法,都是什么? 两种实现方法,一种是继承Thread,另外一种是实现接口Runnable。 3:同步有几种实现方法,都是什么? 同步的实现方法有两种,分别是synchronized, wait与notify。用synchronized可以对一段代码、一个对象及一个方法进行加锁。用wait与notify可以使对象处于等待及唤醒方式导致同步,因为每个对象都直接或间接的继承了Object类。 4:sleep()和wait()有什么区别? sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监恐状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll方法)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 5:同步和异步有何异同,在什么情况下分别使用他们?举例说明。 如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存储(也就是给对象资源加锁),在java中用synchronized关键字给对象、程序段、方法加锁。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回值时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。 6:当一个线程进入一个对象的一个synchronized方法后,其他线程是否可以进入此对象的方法? 不能,一个对象的一个synchronized方法只能由一个线程访问。 7:简述synchronized和java.util.concurrent.locks.Lock的异同? 主要相同点:Lock能完成synchronized所实现的所有功能 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。 8:java中有几种方法可以实现一个线程? 用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 第二问就是synchronized。 下面说说第三问: 反对使用stop(),是因为它不安全。 它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改他们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()方法的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应该在自己的Thread类中置一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,使用wait()命令进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。 流:可以理解为一组有序的、有起点的和有终点的管道 输入和输出是就程序本身而言的 输入流:读取数据到内存 输出流:内存数据写入磁盘 流的分类: 输入流 输出流 或:字节流:处理图片、压缩文件、包括字符串(会乱码) 字符流:处理字符串 字节输入流:inputSteam 字节输出流:outputStream 字符输入流:Reader接口 字符输出流:Writer接口 包装类:字节流与字符流的转换 输出时new FileOutputStream(“文件地址及名称”,true),为true时表示存在文件就追加内容,没有就新建,不写true表示存在就覆盖,不存在就新建 使用byteArrayOutputStream解决InputStream传输字符串乱码问题 后缀为dat的文件存放重要数据,可以反序列化 地址栏输入网址请求,一定是get请求 支付宝接口、微信接口 Eclips中project?clearn重新编译项目 Eclipse中项目右键?properties?Java compiler?设置编译JDK的版本 字节输入流: 节点流类型: FileInputStream PipedInputStream ByteArrayInputStream 处理流类型(包装类): BufferedInputStream SequenceInputStream ObjectInputStream 字节输入流方法: int read() int read(byte[] b) int read(byte[],int offset,int length) 字符输入流: 节点流类型: FileReader PipedReader charArrayReader 处理流类型(包装类): BufferedReader InputStreamReader 字符输入流方法: int read() int read(chare[] b) int read(char[],int offset,int length) 字节输出流: 节点流类型: FileOutputStream PipedOutputStream ByteArrayOutputStream 处理流类型(包装类): BufferedOutputStream DateOutputStream ObjectOutputStream PrintStream 字节输出流方法: void write (byte[] b) void write (byte[],int offset,int length) 字符输出流: 节点流类型: FileWriter PipedWriter charArrayWriter 处理流类型(包装类): BufferedWriter InputStreamWriter printWriter 字符输出流方法: void write () void write (char[] b) void write (char[],int offset,int length) 字节输入流: private static void readAll(){ InputStream is=null; try { is=new FileInputStream("f:\\xiaoshuo.txt"); byte[] data=new byte[1024]; int len=0; StringBuffer str=new StringBuffer(); while( (len=is.read(data))!=-1 ){ System.out.println("本次读了长度为:"+len+"个字节的数据"); str.append( new String(data,0,len,"UTF-8") ); } System.out.println("------------读完了---------------"); System.out.println(str.toString()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } 字符输入流: private static void readAll(){ Reader rd=null; try { rd=new FileReader("f:\\xiaoshuo.txt"); char[] data=new char[1024]; int len=0; StringBuffer str=new StringBuffer(); while( (len=rd.read(data))!=-1 ){ System.out.println("本次读了长度为:"+len+"个字符的数据"); str.append(new String(data, 0, len)); } System.out.println(str.toString()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(rd!=null){ try { rd.close(); } catch (IOException e) { e.printStackTrace(); } } } 利用包装类解决字节流乱码问题: private static void readAll(){ InputStream is=null; ByteArrayOutputStream baos=null; try { is=new FileInputStream("f:\\xiaoshuo.txt"); baos=new ByteArrayOutputStream(); byte[] data=new byte[1024]; int len=0; while( (len=is.read(data))!=-1 ){ System.out.println("本次读了长度为:"+len+"个字节的数据"); baos.write(data, 0, len); } System.out.println("------------读完了---------------"); System.out.println( baos.toString("UTF-8")); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(baos!=null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } } 输出流实例: private static void write1(){ OutputStream os=null; try { os=new FileOutputStream("f:\\11.txt",true); String info="\r\n这是我的四个数据"; os.write( info.getBytes("UTF-8")); os.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(os!=null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } 序列化:将对象写入文件,对象要继承serializable实现接口 private static void xulihua() { Student st = new Student(); st.setId(1000); st.setName("测试"); st.setSex("男"); st.setHp(100); ObjectOutputStream oos = null; try { OutputStream os = new FileOutputStream("f:\\stu.dat"); oos = new ObjectOutputStream(os); st.setHp(50); System.out.println(".....保存游戏进度......"); System.out.println("被弄死了......"); System.out.println("Game over....."); oos.writeObject(st); oos.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (oos != null) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } 反序列化:对象不能改变 private static void reload(){ ObjectInputStream ois=null; try { InputStream in=new FileInputStream("f:\\stu.dat"); ois=new ObjectInputStream(in); Student stu=(Student) ois.readObject(); System.out.println(stu.toString()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ if(ois!=null){ try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } 网络下载器: public static void main(String[] args) { OutputStream download=null; HttpURLConnection conn=null; try { URL url=new URL ("http://dlc2.pconline.com.cn/filedown_171040_8417060/Yx2PSEuB/jpwb2017cl.exe"); conn=(HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(3000); System.out.println("响应码:"+conn.getResponseCode()); if(conn.getResponseCode()==200){ InputStream readInfo=conn.getInputStream(); download=new FileOutputStream("f:\\jpwb2.exe"); byte[] data=new byte[1024*100]; int len=0; while( (len=readInfo.read(data))!=-1){ System.out.println("从服务器上面读取了"+(len/1024.0)+"KB的数据"); download.write(data, 0, len); download.flush(); } System.out.println("-----文件下载成功----"); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(download!=null){ try { download.close(); } catch (IOException e) { e.printStackTrace(); } } if(conn!=null){ conn.disconnect(); } } } 线程实例Thread: public class Test1 { public static void main(String[] args) { Test1 test=new Test1(); MyThread1 t1=test.new MyThread1(); t1.setPriority(10); //优先级设这最高 t1.start(); //启动多线程 MyThread1 t2=test.new MyThread1(); t2.setPriority(1); //优先级设为最低 t2.start(); } class MyThread1 extends Thread { @Override public void run() { for(int i=1;i<=20;i++){ System.out.println("线程"+this.getId()+":"+i); } } } } 线程实例Runnable接口: public class MyRunnable implements Runnable{ public void run() { for(int i=1;i<=20;i++){ System.out.println("线程"+Thread.currentThread().getId()+":"+i); } } } public static void main(String[] args) { MyRunnable myRun = new MyRunnable(); Thread t1 = new Thread(myRun); t1.start(); Thread t2 = new Thread(myRun); t2.start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 20; i++) { System.out.println("线程" + Thread.currentThread().getId() + ":" + i); } } }).start(); }
时间: 2024-10-05 14:00:00