【Java】day23--同步函数、守护线程、线程通讯、IP地址类部分知识点总结

(一)同步函数

同步函数:使用synchronized修饰该函数称为同步函数
同步函数要注意的事项:
    1.非静态同步函数的锁对象是this对象,静态同步函数的锁对象是当前所属类的class文件对象。
    (任何一个class文件被加载到内存时,jvm都会为该class文件创建一个对应的对象描述。(在方法区,只有一个,可以作为锁对象))
    2.同步函数的锁对象是固定的,无法更改。
    
推荐使用:同步代码块
原因:
    1.同步代码块的锁对象可以自己制定,而同步函数的锁对象是固定的。
    2.同步代码块可以随意指定哪个范围需要被同步,而同步函数必须是整个函数都同步,代码不灵活。

class getCash extends Thread{
	static int money=5000;//共享资源
	static Object o=new Object();
	public getCash(String name) {
		super(name);//调用父类一个参数的构造函数,给线程初始化
	}
	//线程的任务代码
	//非静态的同步函数---锁对象是this(),会出现一个线程一直占据资源的情况
	@Override
	public synchronized void run() {
		while(true) {
			//synchronized (o) {
				if(money>0) {
					money-=100;
					System.out.println(Thread.currentThread().getName()+"取走100元,还有"+money+"元");
				}else {
					System.out.println("5000元全部取完...");
					break;
				}
			//}
		}
	}
	//静态同步函数-----当前方法所属类的class文件对象
	public synchronized static void run1() {
		
	}
}
public class demo5 {

	public static void main(String[] args) {
		//创建线程对象
		getCash wife=new getCash("妻子用信用卡");
		getCash husband=new getCash("丈夫用存折");
		//开启线程
		wife.start();
		husband.start();
	}
}

(二)死锁现象

java同步机制解决了线程安全问题,但是同时也引发了死锁现象。
死锁现象只能尽量避免,无法解决。
死锁现象出现的根本原因:    
    1.存在多线程。
    2.多个线程必须共享两个或两个以上的资源。(才会存在等待的情况)

(三)线程的第二种创建方式

自定义线程的创建方式:
    方式一:
        1.自定义一个类继承Thread.
        2.子类重写run方法,把自定义线程的任务定义在run方法上。
        3.创建thread子类的对象,并且调用start方法开启线程。
    方式二:
        1.自定义一个类实现Runnable接口。
        2.实现了Runnable接口的方法,把自定义线程的任务定义在run方法上。
        3.创建Runnable实现类的对象。
        4.创建Thread对象,并把Runnable实现类对象作为参数传递进去
        5.调用Thread对象的start方法开启线程。

Runnable的实现类对象是线程对象么?
    Runnable的实现类对象并不是线程对象,只是实现了Runnable接口的对象。(只有Thread类和其子类的对象才是线程对象)    
为什么把Runnable实现类的对象作为参数传递给Thread对象呢?作用是什么?
    作用:是把Runnable实现类的对象的run方法作为任务代码执行。
推荐使用:第二种线程创建方式。因为java是单继承的。

public class demo2 implements Runnable{

	@Override
		public void run() {
			for(int i=0;i<100;i++) {
				System.out.println(Thread.currentThread().getName()+":"+i);
			}
			System.out.println("当前线程对象:"+Thread.currentThread());//线程对象:t
			System.out.println("当前对象:"+this);//this对象:d,线程调用者对象
		}
	public static void main(String[] args) {
		//创建Runnable实现类的对象
		demo2 d=new demo2();
		//创建Thread对象,并把Runnable实现类对象作为参数传递进去
		Thread  t=new Thread(d,"小猫");
		//调用Thread对象的start方法开启线程。
		t.start();
		/*
			1.Thread类使用了target变量记录了Runnable实现类对象。
			run方法的代码是属于线程的任务代码。
		 * 
		 * */
		//主线程执行的
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}

(四)守护线程

守护线程(后台线程):当一个java应用程序只剩下守护线程的时候,那么守护线程马上结束。
守护线程的应用场景:
    1.新的软件版本下载。
需求:模拟qq在下载更新包

守护线程要注意的事项:
    1.所有线程默认都不是守护线程.

ublic class demo4 extends Thread{
	
	public demo4(String name) {
		super(name);
	}

	@Override
		public void run() {//子类抛出的异常类型必须小于等于父类抛出的异常类型
			for(int i=0;i<100;i++) {
				System.out.println(this.getName()+"已经下载了:"+i+"%");
				//为什么可以捕获,不能抛出(因为父类没有抛出异常,子类当然更不能抛)
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("下载完毕,正在更新安装包...");
		}
	public static void main(String[] args) {
		//创建线程对象
		demo4 d=new demo4("守护线程");
		d.setDaemon(true);//设置一个线程为守护线程
		System.out.println("是守护线程么?"+d.isDaemon());//isDaemon 可以判断一个线程是否为守护线程
		//启动线程
		d.start();
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
		//main线程结束之后,只剩下守护线程,也会立即结束,不再执行。
	}
}

(五)join方法

join方法:线程让步。
需求:模拟小时候打酱油。

class Mother extends Thread{
	@Override
	public void run() {
		System.out.println("妈妈洗菜");
		System.out.println("妈妈切菜");
		System.out.println("妈妈发现没有酱油了。。。");
		//通知儿子去打酱油
		Son s=new Son();
		s.start();
		try {
			s.join();//语句由Mother线程执行
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("妈妈炒菜");
		System.out.println("全家一起吃饭");
	}
}
class Son extends Thread{
	@Override
	public void run() {
		try {
			System.out.println("儿子下楼梯");
			Thread.sleep(1000);
			System.out.println("一直往前走");
			System.out.println("买到酱油");
			System.out.println("跑回来");
			Thread.sleep(1000);
			System.out.println("把酱油给老妈");
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class demo5 {

	public static void main(String[] args) {
		Mother m=new Mother();
		m.start();
	}
}

(六)线程通讯

线程通讯:当一个线程完成了一个任务的时候,要通知另外一个线程去处理其他事情。
线程通讯的方法:
    wait()        执行了wait方法的线程,会使该线程进入以锁对象建立的线程池中等待。
    notify()     如果一个线程执行了notify方法,该线程会唤醒以锁对象建立的线程池中等待线程中的一个。
    notifyAll() 唤醒所有的线程。(以锁对象为标识的线程池中的线程)
线程通讯要注意的事项:
    1.wait notify notify方法都是属于Object对象的方法。(Object的方法才可以分配给任意一个类)
    2.wait notify方法必须要在同步代码块或是同步函数中调用。(锁对象)
    3.wait notify方法必须由锁对象调用,否则报错。(需要锁对象标识线程池)
    4.一个线程执行了wait方法会释放锁对象。
一个线程如果执行了wait方法,那么该线程会进入以锁对象作为标识的一个线程池中等待。
一个线程执行了notify方法,会唤醒以锁对象建立的线程池中等待线程中的其中一个。

需求:生产者生成一个产品,消费者消费一个。

//产品类
class Product{
	String name;
	int price;
	boolean flag;//产品是否生成完毕,false为没有
}
//生产者类
class Producer extends Thread{
	public Producer(Product p) {
		this.p=p;
	}
	//维护一个产品
	Product p;
	//任务代码
	@Override
	public void run() {
		int i=0;
		while(true) {
			synchronized (p) {//共享代码块
				if(p.flag==false) {
					if(i%2==0) {
						p.name="摩托车";
						p.price=4000;
					}else {
						p.name="自行车";
						p.price=300;
					}
					System.out.println("生产了"+p.name+" 价格:"+p.price);
					i++;
					//生产完毕--改标识
					p.flag=true;
					//唤醒消费者消费
					p.notify();
				}else {
					//如果产品已经生成完毕,应该等消费者先消费,再生产
					try {
						p.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}	
			}	
		}
	}
}
//消费者类
class Consumer extends Thread{
	//维护一个产品
	Product p;
	public Consumer(Product p) {
		this.p=p;
	}
	@Override
	public void run() {
		while(true) {
			synchronized (p) {
				if(p.flag==true) {
					System.out.println("消费者消费了"+p.name+",价格"+p.price+"元");
					//改标识
					p.flag=false;
					p.notify();
				}else {
					//等待生产商生成完毕
					try {
						p.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}	
		}
	}
}
public class demo6 {

	public static void main(String[] args) {
		//创建一个产品对象
		Product p=new Product();
		//创建线程对象(同一个p)
		Producer producer=new Producer(p);
		Consumer consumer=new Consumer(p);
		//启动线程
		producer.start();
		consumer.start();
	}
}

(七)停止线程

停止线程:
    注意事项:
        1.停止线程一般通过变量控制。
        2.如果停止一个等待状态下的线程,需要配合interrupt方法。

public class demo7 extends Thread{

	boolean flag=true;
	public demo7(String name) {
		super(name);
	}
	@Override
	public synchronized void run() {
		int i=0;
		while(flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				System.out.println("接收到一个InterruptedException");
				//e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":"+i);
			i++;
		}
	}
	public static void main(String[] args) {
		//创建线程对象
		demo7 d=new demo7("小米");
		d.start();
		//当主线程的i到80的时候,停止小米线程
		for(int i=0;i<100;i++) {
			if(i==80) {
				//d.stop();//可以实现,但过时了
				d.flag=false;
				d.interrupt();//强制清除一个线程的wait sleep状态,可以指定清除哪个线程,但notify不能指定
				/*
				synchronized (d) {
					d.notify();
				}
				*/
			}
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}

(八)IP地址类

java是面向对象的语言,所以java使用了一个类描述了IP地址。

InetAddress IP地址类
    需要掌握的方法:
        static getLocalHost()        返回本机ip地址对象    
        getByName(String host)        指定字符串形式的ip地址或是主机名创建一个ip地址对象
        String getHostName()        返回主机名
        getHostAddress                返回本机ip地址字符串的表示形式
        getAllByName(String host)

public class demo1 {

	public static void main(String[] args) throws UnknownHostException {
		InetAddress address=InetAddress.getLocalHost();//获取到本机的ip地址对象
		//InetAddress address=InetAddress.getByName("jjjj");//主机名不保险,会报错
		System.out.println("本机的ip地址:"+address.getHostAddress());
		System.out.println("主机名:"+address.getHostName());
		InetAddress[] address1=InetAddress.getAllByName("https://www.baidu.com");
		
		//System.out.println(Arrays.toString(address1));
	}
}

 

 

你可能感兴趣的:(java)