(一)同步函数
同步函数:使用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));
}
}