Java高级知识

高级Java编程

1 多线程

1.1 XX

1.2 程序

是计算机指令的集合,程序是一组静态的指令集,不占用系统运行资源,不能被系统调度,也不能作为独立运行的单位,以文件的形式存储在磁盘上

操作系统的发展使得多个程序能够同时运行 ,程序在各自的进程中运行,相互分离,各自独立运行,由操作系统来分配资源,如内存、安全证书

1.3 进程

  1. 是一个程序在其自身的地址空间中的一次执行活动(也就是调用进程),如打开记事本
  2. 程序不能申请系统资源,一个程序可以对应多个进程(如QQ多开)
  3. 是资源申请、调度和独立运行的 基本单位,使用系统中的运行资源;有独立的内存空间和系统资源

1.4 线程

进程中执行运算的最小单位,处理器分配给线程是正在处理器上运行的是线程

注意start()方法是使线程开始执行,java中的虚拟机调用线程的run()方法

多任务(同时执行多个任务)
顺序编程模型:

  1. 顺序编程模型是自然的、常规的,顺序进行
  2. 编程语言中,真实世界中的每一个动作,都会抽象成一个规则的动作序列
1.4.1 多线程优点
  1. 可以更好的实现并行
  2. 恰当使用线程可以降低开发和维护的开销,并且能够提供复杂应用的性能
  3. CPU在线程之间开关时资源消耗比进程少很多(创建和撤销线程的开销比进程要少)。开关线程都在同一地址空间内,只需要修改线程控制表或队列,不涉及地址空间和其他工作
  4. java在语言级提供了对多线程程序设计的支持
    注:如果没有多线程的支持,会对如垃圾回收机制特效产生影响,会和所有的业务操作挂钩
  5. 增加程序的执行效率,程序切换执行,时间比较短(但对于CPU来说,某个时刻只有一个线程在执行)
★1.4.2 线程的实现

Thead
java.lang.Thread
使用线程步骤:
定义线程(线程开始)->创建线程对象->启动线程->终止线程(一般让他自己结束看1.4.4)
每个独立的线程都是Thread类的一个对象
线程中独立于其他线程所执行的指令代码由Thread类的run方法提供
多线程协作运行->创建多个独立的Thread类对象->线程与线程之间所执行的指令代码会存在差异,所以实现独立执行不同指令代码的多线程引用程序最简单直接方法如下
实现步骤:

  1. 继承Thread类
  2. 重写run方法
  3. 构建其对象

例子:体会多线程协作运行

public class TestXC {
	public static void main(String[] args) {
		//第三步:并构建其对象
		A a = new A();
		B b = new B();
		//注意!!!:启动线程start方法处于"就绪状态(等待CPU分配资源)",并没有马上执行
		//如果直接调用run方法,只是普通调用!
		a.start();
		b.start();
	}
}
//第一步:继承Thread类
class A extends Thread{
	//第二步:重写run方法
	@Override
	public void run() {
		while (true) {
			System.out.println("我作妖");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
//同理,不写
class B extends Thread{
	@Override
	public void run() {
		while (true) {
			System.out.println("我作鬼");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
★1.4.3 线程的生命周期

新线程 就绪状态 运行状态 等待/堵塞 死亡状态

java中线程状态转换
在这里插入图片描述

描述
新线程当利用new关键字创建线程对象实例后,它仅仅作为一个对象实例存在, JVM没有为其分配CPU时间片和其他线程运行资源 (Thread t = new Thread)
就绪状态在处于创建状态的线程中调用start方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU时间之外的其它系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会 (t.start())
运行状态就绪态的线程获得cpu就进入运行态,执行就是run [call ]方法中的代码段
等待/阻塞线程运行过程中被剥夺资源或者,等待某些事件就进入等待/阻塞状态, suspend()方法被调用 , sleep()方法被调用,线程使用wait()来等待条件变量;线程处于I/O等待等,调用suspend方法将线程的状态转换为挂起状态。这时,线程将释放占用的所有资源,但是并不释放锁,所以容易引发死锁,直至应用程序调用resume方法恢复线程运行。等待事件结束或者得到足够的资源就进入就绪态
死亡状态当线程体运行结束[正常结束],由JVM收回线程占用的资源
1.4.4 线程的实现方式

建议结束的方法不用stop(已弃用),所以采用以下三种方法
注:start调用本地方法start0调用run,才有线程,否则是普通方法

1.4.4.1 继承Thread
public class TestXC {
	public static void main(String[] args) {
		//第三步:并构建其对象
		A a = new A();
		B b = new B();
		//注意:启动线程start方法处于"就绪状态(等待CPU分配资源)",并没有马上执行
		a.start();
		b.start();
	}
}
//第一步:继承Thread类
class A extends Thread{
	//第二步:重写run方法
	@Override
	public void run() {
		while (true) {
			System.out.println("我作妖");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

class B extends Thread{
	@Override
	public void run() {
		while (true) {
			System.out.println("我作鬼");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
			
	}
}
1.4.4.2 实现Runnable接口
  • Runnable存在局限性,java是单亲继承体系,一旦类继承Thread之后也没有父类可以继承的位置了,因为没有start方法,需要调用方法实现

在这里插入图片描述
实现步骤:

  1. 实现Runnable接口
  2. 重写Run方法
  3. 构造对象传入参数
public class TestRunnableMain {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建TestRunnable对象
		TestRunnable tr = new TestRunnable();
		
		//创建Thread对象,将TestRunnable对象作为参数tr传递给Thread
		Thread th = new Thread(tr);
		
		//启动程序
		th.start();
		System.out.println(Thread.currentThread().getId());
	}
}
class TestRunnable implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("hello"+Thread.currentThread().getId());
		}
	}
}
1.4.4.3 Runnable名下Lambda拉姆达表达式

注意:要求自定义的接口只能有一个抽象方法需要实现,否则报错()
在这里插入图片描述

public class TestLambda {

	/*
	 * 独立的线程:主线程
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Runnable r1 = () -> {
			System.out.println("Lambda+匿名内部类1111");
		};
		// Runnalble r = () -> System.out.println("匿名内部类");

		Runnable r2 = () -> {
			System.out.println("Lambda+匿名内部类2222");
		};

		// 匿名对象
		new Thread(r1).start();
		new Thread(r2).start();
	}
}

谁先抢到就是谁先执行
在这里插入图片描述在这里插入图片描述

1.4.4.4 Callable结束

看下Callable接口源码

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
  • Callable -> 支持泛型
  • 有返回值
  • @FunctionalInterface 支持拉姆达写法

实现步骤

  1. 实现Callable接口,
  2. 实现call方法(注意有返回值)
  3. 创建Callable对象、FutureTask对象,将Callable作为参数传入到FutureTask
  4. 调用,将FutureTask对象作为参数传入Thread

例子 :使用Callable输出0-9的随机数

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestCallable {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//3.创建Callable对象、FutureTask对象,将Callable作为参数传入到FutureTask
		Callable<String> mycall = new MyCall();
		FutureTask<String> ft = new FutureTask<String>(mycall);
		//4.调用,将FutureTask对象作为参数传入Thread
		Thread th = new Thread(ft);
		//启动
		th.start();
		String result = ft.get();
		System.out.println("result:"+result);
	}
}

//1.实现Callable<T>接口
class MyCall implements Callable<String>{
	@Override
    //2.重写call方法
	public String call() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("call.");
		return "call:"+(int)(Math.random()*10);
	}
}

在这里插入图片描述

1.4.4.5 例子:使用多线程完成三个图片的下载

本例子使用第一种方法: 继承Thread

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.io.FileUtils;

/**
 * 使用多线程完成三个图片的下载 1.得到url地址 2.通过io相关代码实现流传递 3.启动线程,实现下载
 * 
 * @author administrator
 *
 */
public class TestPictureDownload {
	public static void main(String[] args) {
		// 构造三个对象 DownloadThread
		DownloadThread dt1 = new DownloadThread(
				"https://www.maserati.com/content/dam/maserati/international/Models/mc20/introduction/01-desktop.jpg",
				"D:\\ccp\\x作业01.jpg");
		DownloadThread dt2 = new DownloadThread(
				"https://www.maserati.com/content/dam/maserati/international/Models/mc20/introduction/02-desktop.jpg",
				"D:\\ccp\\x作业02.jpg");
		DownloadThread dt3 = new DownloadThread(
				"https://www.maserati.com/content/dam/maserati/international/Models/mc20/introduction/03-desktop.jpg",
				"D:\\ccp\\x作业03.jpg");
		// 分别启动线程,使线程处于 就绪状态
		new Thread(dt1).start();
		new Thread(dt2).start();
		new Thread(dt3).start();
	}

}

//继承Thread
class DownloadThread extends Thread {
	private String url;// 要下载的源网址链接地址
	private String destname;// 目标文件的文件名

	/**
	 * 构造,提供了两个参数
	 * 
	 * @param url              源网址链接地址
	 * @param destname目标文件的文件名
	 */
	public DownloadThread(String url, String destname) {
		super();
		this.url = url;
		this.destname = destname;
	}

	@Override
	public void run() {
		// 1- 从某一个网址上获取一个链接,将这个链接文件使用流形式输出到本地 : 借助FileUtil ->外部jar commons-io
		// 2- URL
		File destination = new File(destname);
		// 两个参数 URL 地址 File对象

		try {
			FileUtils.copyURLToFile(new URL(url), destination);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(destname + "下载完成");
	}
}

在这里插入图片描述

1.4.4.6 扩展:Runnable和Thread的区别和联系
(区别)RunnableThread
类别接口
源头支持多继承的写法,定义一个类实现接口的同时还可以继承其他的类实现了Runnable接口,重写了run方法
线程池(只)能放入实现Runnable 类线程不能直接放入继承Thread的类[后续提到]
优点Runnable适合多个相同的程序代码的线程去处理同一个资源,避免Java中的单继承的限制,增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

1.7.4 Runnable和Thread的区别和联系?

  1. Thread是一个类,Runnable是一个接口;
  2. Thread类实现了Runnable接口,重写了run方法.
  3. Runnable是一个接口,定义一个类实现接口的同时还可以继承其他的类 ; Runnable 支持多继承的写法;
  4. Runnable适合多个相同的程序代码的线程去处理同一个资源,避免Java中的单继承的限制,增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。线程池只能放入实现Runnable 类线程,不能直接放入继承Thread的类.[后续提到]
1.4.5 线程安全
1.4.5.1 普通方法/使用线程(洗杯子)——执行时间比较
  1. 两个步骤 : 烧水(10s)->洗杯子(5个杯子 2s) (按照一定的步骤顺序执行) 20s (使用普通方法,直接调用run方法)
public class TestMakeTea {
	public static void main(String[] args) {
		BoilWater bw = new BoilWater();
		WashCup wc = new WashCup();
		bw.run();
		wc.run();
	}
}
class BoilWater{
	public void run() {
		System.out.println("开始烧水!");

		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("水烧好了");
	}
}
class WashCup{
	public void run() {
		System.out.println("开始洗杯子!");

		for (int i = 1; i <= 5; i++) {
			System.out.println("第" + i + "杯子洗刷好了");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

在这里插入图片描述

  1. 两个步骤 : 烧水(10s) 洗杯子(5个杯子 2s) (两个线程,并行执行) <20s (使用线程同步,调用start方法开启线程同步)
public class TestMakeTea {
	public static void main(String[] args) {
		BoilWater bw = new BoilWater();
		WashCup wc = new WashCup();
		bw.start();
		wc.start();
	}
}

class BoilWater extends Thread {
	@Override
	public void run() {
		System.out.println("开始烧水!");

		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println("水烧好了");

	}
}
class WashCup extends Thread{
	
	@Override
	public void run() {
		System.out.println("开始洗杯子!");
		
		for (int i = 1; i <= 5; i++) {
			System.out.println("第"+i+"杯子洗刷好了");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

在这里插入图片描述

1.4.5.2 线程安全问题——影响

多个线程操作同一个对象,有可能导致线程不安全-数据丢失

体会线程安全不安全的影响——影响数据的存取
java.util.concurrent.CopyOnWriteArrayList
注:java.util.concurrent又叫juc,里面很多工具类都是线程安全

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class TestCopyonWriteArrayList {
	public static void main(String[] args) throws InterruptedException {
		
		List<String> arraylist = new ArrayList<String>();
		List<String> copyonwriterarraylist = new CopyOnWriteArrayList<String>();
		for (int i = 1; i <=50000; i++) {
			new Thread(() -> arraylist.add(Thread.currentThread().getName())).start();
			new Thread(() -> copyonwriterarraylist.add(Thread.currentThread().getName())).start();
		}
		Thread.sleep(1000);
		System.out.println("线程不安全arraylist:"+arraylist.size());
		System.out.println("线程安全copyonwriterarraylist:"+copyonwriterarraylist.size());
	}
}

结果解析:程序执行后,进入就绪状态的线程发现0位置没有数据,出现抢夺资源的情况,所以同时添加数据,导致数据重复
在这里插入图片描述

1.4.5.3 线程数据共享(多窗口卖票)

模拟售票: 很多个售票点; 用的是同一份数据; 数据的一致性,也就是每个售票点理解为一个线程操作的话,这些售票点对票的数量应该是”共享”的;

package TestXianCheng;

public class TestTicket {
	public static void main(String[] args) {
		TickerThread tt = new TickerThread();
        //售票点1
		Thread t1 = new Thread(tt);
        //售票点2
		Thread t2 = new Thread(tt);
        //售票点3
		Thread t3 = new Thread(tt);
		t1.start();
		t2.start();
		t3.start();
	}
}

class TickerThread implements Runnable {
	private int ticketCount = 1000;
	
	//线程异步
	private synchronized void sale() {
		//判断
		if (ticketCount > 0) {
			//开卖
			System.out.println(Thread.currentThread().getName()+"正在卖票,还有" + --ticketCount + "张");
			try {
				Thread.currentThread().sleep((int)(Math.random()*10));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (ticketCount > 0) {
			sale();
		}
	}
}
1.4.6 线程同步和通信
  • wait方法处于Object类
  • 使当前线程等待,直到另一线程调用对象的notify()/notifyAll()方法
1.4.6.1 正常通信wait & notify
package TestXianCheng;

/**
 * person实体类
 * @author administrator
 *
 */
public class Person {
	private String name;
	private String sex;
	//增加一个布尔类型的变量,表示的是 是否已经设置过了person对象的属性信息
	// 标识flag的值:false没有设置name和sex,true有
	boolean flag = false;
	
	//获取name和sex并做输出
	public synchronized void showPerson(){
		if (!flag) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//如果为true才有机会执行以下代码!
		System.out.println("name:"+name + "\tsex:"+sex+"\n");
		//显示完了,把flag调成已设置false
		flag = false;
		//唤醒设置程序(setPerson)继续设置
		notifyAll();
	}
	//设置name和sex的值
	public synchronized void setPerson(String name,String sex) {
		if (flag) {
			// 不需要再设置了,那就等待
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		try {
			Thread.sleep((int)(Math.random()*2000));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.name = name;
		this.sex = sex;
		
		//设置完了,把flag调回已设置的状态true
		flag = true;
		//唤醒显示程序(showPerson)
		notifyAll();
	}
}

package TestXianCheng;
/**
 * 	设置Person信息类
 * @author administrator
 *
 */
public class ModifyPerson implements Runnable {
	private Person p;
	private int n = 0;
	public ModifyPerson(Person p) {
		super();
		this.p = p;
	}
	
	public void modify() {
		//交替设置Person
		if (n % 2 == 0) {
			p.setPerson("大头儿子", "男");
		}else {
			p.setPerson("小头阿姨", "女");
		}
		n++;
	}
	
	public void run() {
		//死循环执行
		while(true) {
			modify();
		}
	}
}
package TestXianCheng;
/**
 *  显示Person信息类
 * @author administrator
 *
 */
public class ShowPerson implements Runnable{
	private Person p;
	private int n = 0;
	public ShowPerson(Person p) {
		super();
		this.p = p;
	}
	
	public void show() {
		p.showPerson();
	}
	
	public void run() {
		//死循环执行
		while(true) {
			show();
		}
	}
}
package TestXianCheng;
/**
 * 	Person测试类
 * @author administrator
 *
 */
public class TestPerson {
	public static void main(String[] args) {
		Person p = new Person();
		ModifyPerson mp = new ModifyPerson(p);
		
		ShowPerson sp = new ShowPerson(p);
		
		new Thread(mp).start();
		new Thread(sp).start();
	}
}
1.4.6.2 Synchronized 同步实例方法,同步代码块
1.4.7 Thread.currentThread中类的方法
  • Thread.currentThread()得到当前线程
  • Thread里面很多都是线程的方法
  • currentThread()是静态方法
  • 作用:得到当前正在执行的线程对象的引用
//得到的是主程序的对象引用
public static void main(String[] args) {
		System.out.println(Thread.currentThread().getName());
	}

在这里插入图片描述

1.4.7.1 currentThread中几种常用方法
方法名返回值类型描述
getId()long返回线程的标识符
getName()String返回线程的名称
getPriority()int返回线程的优先级
getState()Thread.State返回线程的状态
sleep
@Override
	public void run() {
		for (int i = 1; i <= 1; i++) {
			System.out.println("getId:" + Thread.currentThread().getId() + "\ngetName:"
					+ Thread.currentThread().getName() + "\ngetPriority:" + Thread.currentThread().getPriority()
					+ "\ngetState:" + Thread.currentThread().getState());
		}
	}

在这里插入图片描述

1.4.7.2 setPriority设置优先级
  • 只是说优先级高的分配到的资源会多些,优先执行而已
  • 如下图,优先级不能超出1-10的取值范围,且线程的默认优先级是5
  • 建议使用系统自带的优先级常量Thread.(MAX_PRIORITY/MIN_PRIORITY/NORM_PRIORITY)否则抛出IllegalArgumentException
  • 如果该线程已经属于一个线程组(ThreadGroup),该线程的优先级不能超过该线程组的优先级
1.4.7.3 getState获取线程状态
状态描述
New还没启动的线程
Runnablejava虚拟机中正在执行的线程
Blocked被阻塞等待监视器锁定的线程
Wating等待另一线程执行特定动作的线程
Timedwaiting等待另一线程执行动作达到指定等待时间的线程
Teminated已退出的线程
1.4.8 JOIN
  • 可以理解成插队

  • 插队后必须等这个线程结束,主线程才继续执行
    在这里插入图片描述

1.4.9 线程池

解决了线程的生命周期的开销和资源不足问题

通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快

返回值作用
isShutdown()boolean如果任务已关闭返回true
shutdown()void启动有序关闭,之前提交的任务被执行,不接受新任务
submit(Runnable task)Future<?>提交一个可运行的任务执行,返回一个表示该任务的未来

优点

  • 降低资源消耗
  • 提高响应速度
  • 无需反复创建线程

创建线程池对象java.utl.cocurrent.ExecutorService

方法作用
newCacheThreadPool()创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程
newCachedThreadPool(ThreadFactory threadFactory)创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程,并在需要时使用提供的ThreadFactory创建新线程
newFixedThreadPool(int nThreads)创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程
newFixedThreadPool(int nThreads,ThreadFactory threadFactory)创建一个线程池,重用固定数量的线程,从共享无界队列中运行,使用提供的ThreadFactory在需要时创建新线程
1.4.9.1 死锁

定义:某一线程在等待另一线程,而另一线程再等待其他线程,如此反复,直到链条上的线程又在等待第一个线程释放锁,这样的线程之间相互等待的连续循环,没有线程能继续的称为“死锁”

死锁的形成是潜在的,不易发现的

例子: 抢椅子

public class TestMain {

	public static Object obj1 = new Object();
	public static Object obj2 = new Object();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread01 th01 = new Thread01();
		Thread02 th02 = new Thread02();
		th01.start();
		th02.start();
	}

}

class Thread01 extends Thread{
	@Override
	public void run() {
		synchronized(TestMain.obj1) {
			System.out.println("Thread01中lock obj1");
		}
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		synchronized(TestMain.obj2) {
			System.out.println("Thread01中的lock obj2");
		}
	}
}

class Thread02 extends Thread{
	@Override
	public void run() {
		synchronized(TestMain.obj2) {
			System.out.println("Thread02中lock obj1");
		}
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		synchronized(TestMain.obj1) {
			System.out.println("Thread01中的lock obj1");
		}
	}
}
1.4.10 线程分类
1.4.10.1 用户线程User Thread
1.4.10.2 守护线程 Daemon Thread
  • 偷偷做的,隐藏的线程
  • 可以通过调用Thread.setDaemon(true)实现线程 -> 守护线程
  • 默认创建的线程对象是非守护的用户线程对象
守护线程用户线程
定义指在程序运行的时候在后台提供一种通用服务的线程
虚拟机的退出有需要守护的线程就会一直在,如果没有了也就没有存在的必要
使用守护线程需要注意的几点:
  1. 当所有非守护线程终止时,程序才算真的终止了,而且会同时杀死所有守护线程
  2. thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出一个IllegalThreadStateException异常,不能把正在运行的常规线程设置为守护线程
  3. 在Daemon线程中产生的新线程也是Daemon的
  4. 不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了
  5. 可以通过线程对象的isDaemon()方法判定该线程是否守护线程
1.4.11 线程阻塞&激活

sleep方法
Thread.sleep()
例子:模拟线程阻塞(当i=5时,线程进入阻塞状态)

public class TestCurrentThreadMethods {
	public static void main(String[] args) {
		ThreadSleep ts = new ThreadSleep();
		new Thread(ts).start();
	}
}

class ThreadSleep implements Runnable{
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			if (i == 5) {
				try {
					System.out.println("等我几秒种");
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}

在这里插入图片描述

附:yeild和sleep的区别
(区别)yeildsleep
作用使当前线程重新回到就绪可执行状态使当前线程进入阻塞状态
阻塞时长不需要提供(使线程直接进入就绪状态,没有阻塞时长需要提供
结果可以使优先级高的线程得到执行的机会,而且只能使相同或更高优先级的线程有执行的机会,甚至于某些时候JVM认为不符合最优资源调度的情况下会忽略该方法的调用(类似于System.gc())因为不考虑线程的优先级, 可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会
共同点不会释放锁资源不会释放锁资源
注:
  1. 执行sleep()的线程在指定的时间内肯定不会执行
  2. 不会释放锁资源: 即如果正在运行的线程占有某个资源的同步锁,它不会释放掉这个同步锁,其他线程仍然不能访问该资源
  3. Java并不保证线程在阻塞给定的时间后能够马上执行(事实上这几乎是不可能的事情),在阻塞时间到了之后,线程进入就绪状态,继续执行的时机取决于Java虚拟机的线程调度机制,唯一能够确定的是,线程中断执行的时间是大于等于给定的阻塞时长的,因此不要将sleep用作精确度要求非常高的定时任务调度
附:Lock接口和Synchronized区别
区别
Lock接口和SynchronizedLock接口在多线程和并发编程中最大的优势是它们为读和写分别提供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞
Lock接口提供lock和unlock方法获得和释放锁
Lock接口具有比Synchronized更广泛的锁定操作
★附:线程和进程的区别
线程进程
进程中执行运算的最小单位系统运行程序的基本单位
处理器分配给线程的,真正在处理器上运行的是线程有独立的内存空间和系统资源
★ 存在某一张票被销售了多次
class SaleThread implements Runnable {
 
 /**
  * 使用静态成员变量作为100张票的保存变量,是一个共享资源。
  */
 private static int tickets = 100;
 
 @Override
 public void run() {
 
  // 完成售票过程
  while (true) {
   /*
   字符串可以作为锁对象,因为双引号包含的字符串不管在代码中如何运行,有且只有一个
    */
   synchronized ("锁") {
 
    try {
     Thread.sleep(500);
    } catch (InterruptedException e) {
     e.printstacktrace();
    }
 
    if (tickets > 0) {
     System.out.println(Thread.currentThread().getName() + "售出了" + tickets + "张票");
     tickets--;
    } else {
     System.out.println(Thread.currentThread().getName() + "售罄!!!");
     break;
    }
   }
  }
 }
}
 
public class Demo {
 public static void main(String[] args) {
  Thread t1 = new Thread(new SaleThread(),"售票人员1");
  Thread t2 = new Thread(new SaleThread(),"售票人员2");
  Thread t3 = new Thread(new SaleThread(),"售票人员3");
 
  t1.start();
  t2.start();
  t3.start();
 }
}
max.1 任务调度以后说

2 网络编程

定义:简单来说,就是实现计算机节点之间的数据通信

2.1 协议

内容特点
TCP(传输控制协议)是一种面向连接、可靠的、基于字节流的传输层通信协议可靠
UDP(用户数据报协议)用于处理数据包,是一种无连接的、不可靠的通信协议不可靠(不作确认只作发送)
IP(网络协议)把数据从源传送到目的地,它不负责保证传送可靠性、流控制、包顺序等不可靠(不保证传送可靠性)
ICMP(网络控制报文协议)TCP/IP协议栈的一个子协议,用于在IP主机、路由器之间传递控制消息

2.2 TCP与UDP差异

例子:单次传输对象

MyClientSocket类

package com.ccp.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;

public class MyClientSocket {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		InputStream is = null;
		ObjectInputStream ois = null;

		Socket client = null;
		superStar ss = new superStar();

		try {
//			client = new Socket("192.168.12.27", 9791);
			client = new Socket("127.0.0.1", 2000);

			// 写给服务器
			is = client.getInputStream();
			
			ois = new ObjectInputStream(is);
			
			// 是Obeject才转型
			if (is instanceof Object) {
				try {
					ss = (superStar) ois.readObject();
				} catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("client:"+ss);
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			//释放资源
			try {
				if (ois != null) {
					ois.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if (client != null) {
					client.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

MyServerSocket类

package com.ccp.socket;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerSocket {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ServerSocket serverSocket = null;
		Socket server = null;
		ObjectOutputStream oos = null;
		OutputStream os = null;
		try {
			// 创建serversocket对象
			serverSocket = new ServerSocket(2000);
			System.out.println("开启服务监听");
			// accept
			server = serverSocket.accept();
			// 通过socket进行流的操作
			os = server.getOutputStream();
			//写
			oos = new ObjectOutputStream(os);
			//写对象
			superStar ss = new superStar("成龙", 25);
			oos.writeObject(ss);

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			// 释放资源
			try {
				if (oos != null) {
					oos.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if (os != null) {
					os.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if (server != null) {
					server.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if (serverSocket != null) {
					serverSocket.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

实体类superStar

package com.ccp.socket;

import java.io.Serializable;

public class superStar implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	
	public superStar() {
		// TODO Auto-generated constructor stub
	}

	public superStar(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "superStar [name=" + name + ", age=" + age + "]";
	}
}

在这里插入图片描述

一对多聊天室

Mysever

在这里插入代码片

UDP

DatagramSocket
此类表示用来发送和接收数据报包的套接字

构造方法作用
DatagramSocket构造数据报套接字并将其绑定到本地主机上任何可用的端口
DatagramSocket(DatagramSocketImpl impl)创建带有指定 DatagramSocketImpl 的未绑定数据报套接字
DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口
DatagramSocket(int port, InetAddress laddr)创建数据报套接字,将其绑定到指定的本地地址。
实例方法作用返回值
receive(DatagramPacket datagramPacket)从此套接字接收数据报包void
send(DatagramPacket datagramPacket)从此套接字发送数据报包void

例子: 利用Datagram发送和接收UDP数据报

  • DatagramPacket数据包不需要close()关闭
在这里插入代码片

URL

java.lang.Object
java.net.URL
类,Class URL表示统一资源定位符,指向万维网上的“资源”的指针

实例方法
方法作用
openConnection返回connection连接
getHost主机名
getPort端口
getProtocol协议
getContent内容
getInputStream返回从此打开的连接读取的输入流
getOutputStream返回写入此连接的输出流
在这里插入图片描述
例子: 图片下载
package com.ccp.socket;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class URLTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		URLConnection conn = null;
		InputStream is = null;
		FileOutputStream fos = null;
		URL url = null;
		try {
			// 图片地址的对象
			url = new URL(
					"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3429518642,4089250331&fm=26&gp=0.jpg");

			System.out.println("主机名:" + url.getHost());
			System.out.println("端口:" + url.getPort());
			System.out.println("协议:" + url.getProtocol());
			System.out.println("内容:" + url.getContent());

			// 返回一个连接对象
			conn = url.openConnection();

			// 得到读取的输入流
			is = conn.getInputStream();

			// 下载地址
			fos = new FileOutputStream("picture.jpg");

			//字节数组
			byte[] buf = new byte[1024];

			int length = 0;
			
			while ((length = is.read(buf)) != -1) {
				fos.write(buf, 0, length);
			}
			System.out.println("下载成功");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (fos != null) {
					fos.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

扩展:NIO

在这里插入图片描述

3 高级

3.1 常用设计模式

一套代码的设计经验的总结。巧妙运用可以解决很多问题。

3.1.1 设计模式的分类
模式名包含
创建型模式工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
行为型模式策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
3.1.2设计模式的六大原则:
  • 开闭原则:支持程序扩展,但不能修改原来的代码
  • 里氏代换原则
  • 依赖倒转原则
  • 接口隔离原则
  • 迪米特法则(最少知道法则)
  • 合成复用原则
3.1.3 单例模式(Singleton)
  • 最简单的设计模式之一,提供了一种创建对象的最佳方式
  • 创建型模式

确保在一个应用程序中某个类只有一个实例

  1. 单例类只能有一个实例
  2. 必须自己创建自己的唯一实例
  3. 必须给所有其他对象提供这一实例

Java中实现单例模式可以通过两种形式实现

共有点:

  • 构造方法私有化
  • 提供一个静态的方法返回一个类对象
3.1.1.1 懒汉模式

类加载时 不初始化
声明但不赋值 - 加入线程安全

//懒汉模式
class LazySingleton {
	// 类加载,执行一次
	private static LazySingleton singleton = null;

	// 不能让外部直接调用构造方法,不让他直接实例化
	private LazySingleton() {
				// TODO Auto-generated constructor stub
			}

	// 加个synchronized
	public static synchronized LazySingleton getInstance() {
		// 判断
		// 多个线程同时访问可能会产生多个实例,甚至破坏实例,违背单例设计的单例的设计原则
		if (singleton == null) {
			singleton = new HungarySingleton();
		}
		return singleton;
	}
}
3.1.1.2 饿汉模式

类加载时 初始化,所以类加载比较 ,但是获取对象更快

//饿汉模式
class HungarySingleton {
	// 类加载,执行一次
	private static HungarySingleton singleton = new HungarySingleton();

	// 不能让外部直接调用构造方法,不让他直接实例化
	private HungarySingleton() {
		// TODO Auto-generated constructor stub
	}

	public static HungarySingleton getInstance() {
		return singleton;
	}
}

在这里插入图片描述

3.1.1.3 懒汉模式实现线程安全的两种方法
  • synchornized
  • 内部类
3.1.4 工厂模式
3.1.5 观察者模式

3.1 反射

3.2 注解

3.3 设计模式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千鹤万象

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值