Java开发之路(12)Java基础——注解Annotation和多线程入门_安逸 的博客-程序员宅基地

技术标签: Java  面试  java  多线程  编程语言  

一、注解的概念:

注释:写给程序员看的
注解:写给编译器、JVM看的。

[email protected]

class test1{
    
//注解:JVM知道这个方法是重写方法,检查这个方法是否符合重写规则。
	@Override
	public String toString() {
    
		return "test1 []";
	}
}

后面学习框架的时候会遇到大量的注解,结合反射一起运用。

2.预定义注解:

JDK自带的注解。

class test1{
    
//注解:JVM知道这个方法是重写方法,检查这个方法是否符合重写规则。
	@Override
	public String toString() {
    
		return "test1 []";
	}
	
	@Deprecated//不推荐使用
	void test() {
    	
	}	
	@SuppressWarnings("")//警告
	void test1() {
    	
	}
}

二、自定义注解

1.注解简单定义

@Table(value = "UserTable")//使用注解
class User{
    
	
}

//声明一个自定义注解,该注解的名字为Table
//@interface是声明注解的注解
@interface Table{
    
	String value();//该注解的属性,value属性,String的值
	//String tupe();
}

2.配置注解相关的使用信息

在这里插入图片描述

@Target(ElementType.TYPE)//声明Table注解在哪使用:类、属性、方法
@Retention(RetentionPolicy.RUNTIME)//声明Table注解是什么时候用:运行、编译

三、注解的应用场景

通过注解将类和表对应起来

表——类:userTable——user
列——属性:
id——id:主键
name——userName:

import java.lang.annotation.Annotation;

/*
 * 通过注解将类和表对应起来
 */
public class AnnotationTest3 {
    
	public static void main(String[] args) {
    
		Class clazz = User.class;
		Annotation[] ana = clazz.getAnnotations();
	}
}

@interface Bean{
    //作用在类上,代表该类与哪张表对应
	String table();
}
@interface Id{
    //作用在类的属性上,代表该属性是主键
	String value();
}
@interface Column{
    //作用在属性上,代表该属性所对应的列
	String value();
}

@Bean(table = "user")//代表User类对应的表是User表
class User{
    
	@Id(value = "u_id")//代表id属性是主键,同时表中的主键列是u_id
	private int id;
	@Column(value = "nickName")
	private String name;
	@Column(value = "password")
	private String pwd;
	@Column(value = "telNumber")
	private String phone;
	@Column(value = "userType")
	private String type;
	@Column(value = "q_email")
	private String email;
	@Column(value = "qq")
	private String qq;
}

四、反射+泛型+注解

一、多线程的概念

1.程序、进程和线程

(1)程序:是为完成特定任务、用某种语言编写的一组指令的集合。即值一段静态的代码,静态对象。
(2)进程:任务管理器中运行的功能软件,进程之间是相对独立的,有它自身的产生、存在和消亡的过程,Java.exe进程Javaw.exe

几核CPU就代表同一个瞬间能处理的任务数。通过主频频繁的切换。

(3)线程:线程是进程的执行单位,一个进程中的小功能是由线程来实现的,线程是程序内部的一条执行路径。

2.并发和并行:

(1)并行:比如鸣人的影分身,分身同时做事情,每个分身是独立的。
(2)并发:同一个人,交替做两件事情。(时间片轮转:两个线程,抢占CPU资源。)

二、多线程的实现:

Java语言的JVA允许程序运行多个线程,通过java.lang.Thread类来实现。

多线程的模型:工人做任务。
多线程实现的2种方式:

1.继承Thread类实现多继承:

Thread类的特性:
每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体。通过Thread对象的start()方法来调用这个线程。

/**
 *继承Thread类的方式实现多线程
 */
class Thread2 extends Thread{
    
	private String name;
    public Thread2(String name) {
    
       this.name=name;
    }
	public void run() {
    
        for (int i = 0; i < 5; i++) {
    
            System.out.println(name + "运行  :  " + i);
            try {
    
                sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            }
        }  
	}
}
public class ThreadTest4 {
    
	public static void main(String[] args) {
    
		Thread2 mTh1=new Thread2("A");
		Thread2 mTh2=new Thread2("B");
		mTh1.start();
		mTh2.start();
		System.out.println("-----------------------------");
		System.out.println("-----------------------------");
		System.out.println("-----------------------------");
		System.out.println("-----------------------------");
	}
}

2.实现Runnable接口

多线程是进程的支流,当分支之后,就各走各的,假设在进程上跑的代码是主程序,当其中的第三行代码是开启线程的,那么,开启线程之后线程运行的代码就和主程序并行(他们之间就不相干了)

/*
 * 多条执行路径:main,t1.start(),t2.start(),t3.start()
 */
public class ThreadTest1 {
    
	public static void main(String[] args) {
    
		Task task = new Task();
		
		//创建多个工人
		Thread t1 = new Thread(task);//创建工人的时候,将任务交给工人。
		Thread t2 = new Thread(task);
		Thread t3 = new Thread(task);
		
		//工人开始干活
		t1.start();//start()方法的时候,会执行run()
		t2.start();
		t3.start();
	}
}

/*
 * 工人要执行的任务
 */
class Task implements Runnable{
    

	@Override
	public void run() {
    
		System.out.println(Thread.currentThread()+"工人doSomething");//工人的任务	
	}
}

在这里插入图片描述

3.Callable+Future

4.继承方式和实现方式的联系和区别

public class Thread extends Object implements Runnable
(1)区别:
继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable:线程代码存放在接口的子类的run方法中。
(2)实现方法的好处:(一般使用实现接口的方式实现多线程)
a.避免了单继承的局限性
b.多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

三、使用多线程的好处:

1.提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
2.提高计算机系统CPU的利用率。
3.改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。

如果在一个方法里有1000行代码,前300,中间300,最后400行这三段代码没有因果关系,这种情况我们就可以使用线程处理,把这三段代码分别放在不同线程中去运行,这样三段代码就是并行运行的。

四、Thread类的有关方法:

在这里插入图片描述
在这里插入图片描述

优先级是用数组1-10表示,数字越大优先级越高,如果没有设置,默认优先级是5。

五、线程的生命周期:

JDK中用Thread.State枚举表示了线程的几种状态。
想要实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整生命周期中通常要经历如下五种状态:
1.新建:当一个Thread类及其子类的对象被声名创建时,新的线程对象就处于新建状态。
2.就绪:处于新建状态的线程被start()之后,将进入线程队列等待CPU时间片,此时它已经具有运行的条件。
3.运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态,run()方法定义了线程的操作和功能。
4.阻塞:被人为挂起或者执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态。
5.死亡:线程完成了它全部的工作或者线程被提前强制性中止。
自然死亡:执行程序完毕或者程序发生了异常到程序结束。
强制死亡:执行.stop()方法,断电。

六、线程的同步:

多个线程同时使用一个资源,保护这个资源的完整性、一致性,线程设置线程同步,实现线程从并行到串行。
例如支付宝微信同时向银行卡取钱。
多线程调用方法时,线程共享资源,一个线程在执行完这个方法没有完毕时,另一个线程又开始执行这个方法。
通过synchronized同步锁来完成线程同步。直接在方法上加上synchronized关键字。
锁的概念:同步锁不是说自能保护一个资源,可以保护多个资源,但是多个资源之间使用同一把锁,一把锁只有一个钥匙,一把锁可以用在多个资源上,当使用同样的锁的时候,不同资源也可以实现同步。
锁是属于对象的,一个对象只有一把锁,一把锁只有一把钥匙。

临界资源:要保护的资源,希望同时只有一个线程来使用的资源。
阻塞:使用临界区资源的线程要等待。
非阻塞:运行多个线程同时进入临界区。

public class ThreadTest8 {
    

	public static void main(String[] args) {
    
		
		new Thread(new Runnable() {
    
			
			@Override
			public void run() {
    
				sayHello("安逸");
			}
		}).start();
		
		new Thread(new Runnable() {
    
			
			@Override
			public void run() {
    
				sayHello("小丸子");
			}
		}).start();;

	}
	
	//两个线程访问同一个资源
	synchronized static void sayHello(String name) {
    
		for (int i = 0; i < 10; i++) {
    
			System.out.println(name+"say hello");
		}
	}
}

1.在普通方法上加同步锁synchronized,锁的是当前整个对象,不是某个方法。

不同的对象是不同的锁,普通方法上加同步锁synchronized,线程使用此方法不同的对象,还是有共享资源问题。
普通方法加同步锁,锁的当前方法对应的对象,当前的对象的所有加了同步锁的方法是共用一个同步锁。

2.静态方法加同步锁synchronized,对于所有的对象都是使用同一个锁。

3.对代码块加入同步锁。

(1)synchronized(this){}锁代码块是代表当前的对象,如果在其他地方中也有synchronized(this)的代码块,使用的都是同一个同步锁。
(2)synchronized(a),这个小括号中传入不同的对象就是不同的锁。

如果针对对象要加同步锁,那就加在方法上,如果针对某一段代码需要加同步锁,那就直接在代码块上加同步锁。
同步方法:这个方法同时只有一个线程可以执行。
同步代码块:这段代码同时只有一个线程可以执行。

public class ThreadTest6 {
    

	public static void main(String[] args) {
    
		//定义账户对象
		Acount a = new Acount();
		//多线程对象
		User3 u_weixin = new  User3(a, 2000);
		User3 u_zhifubao = new  User3(a, 2000);
		
		Thread weixin = new Thread(u_weixin,"微信");
		Thread zhifubao = new Thread(u_zhifubao,"支付宝");

		weixin.start();
		zhifubao.start();
	}
}

class Acount {
    
	public static int money1 = 3000;//全局变量
	/*
	 * 先判断账户钱够不够
	 */
	public synchronized void drawing(int m) {
    
		String name = Thread.currentThread().getName();
		if(money1<m) {
    
			 System.out.println("账户金额不足");
		}else {
    
			System.out.println(name+"操作,账户原有金额:"+money1);
			System.out.println(name+"操作,取款金额:"+m);
			
			money1 -= m;
			System.out.println(name+"操作,取款之后的金额:"+money1);
		}
	}
}

class User3 implements Runnable{
    

	Acount acount = new Acount();
	int money;
	public User3(Acount acount,int money) {
    
		this.acount = acount;
		this.money = money;
	}
	@Override
	public void run() {
    
		acount.drawing(money);
	}
}

七、线程的死锁问题:

1.死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
解决方法:
(1)专门的算法、原则,比如加锁顺序一致
(2)尽量减少同步资源的定义,尽量避免锁未释放的场景。
在这里插入图片描述

class DeadLock{
    
	String lock1 = new String("A");
	String lock2 = new String("B");
	
	void m1() {
    
		synchronized (lock1) {
    
			System.out.println("进入m1方法");
		}
		
		synchronized (lock2) {
    
			System.out.println("进入m1的子块");
		}
	}
	void m2() {
    
		synchronized (lock2) {
    
			System.out.println("进入m1方法");
		}
		
		synchronized (lock1) {
    
			System.out.println("进入m1的子块");
		}
	}
}

八、线程通信:

1.线程和线程之间是相对独立的,线程之间能够信息交互。

2.与线程通信相关的方法:


这三个方法只能用在有同步锁的方法或者代码块中。

3.线程通信经典案例——生产者消费者模式:

/*
 * 生产者与消费者
 */

public class ThreadTest7 {
    
	public static void main(String[] args) {
    
		Clerk c = new Clerk();
		//消费时不生产,生产时不消费。
		//生产者
		new Thread(new Runnable(){
    
			@Override
			public void run() {
    
				synchronized (c) {
    
					while(true) {
    //无限生产次数
						if(c.productNum == 0) {
    //产品数为零,开始生产
							System.out.println("产品数为零,开始生产");
							
							while (c.productNum<4) {
    
								c.productNum++;//生产产品
								System.out.println("库存:="+c.productNum);
							}
							System.out.println("产品数为:"+c.productNum + ",结束生产");
							c.notify();//唤醒消费者
						}else {
    
							try {
    
								c.wait();//生产者线程等待
							} catch (InterruptedException e) {
    
								e.printStackTrace();
							}
						}
					}
				}
			}
		},"生产者").start();;
		
		//消费者
		new Thread(new Runnable(){
    
			@Override
			public void run() {
    
				synchronized (c) {
    
					while(true) {
    //无限消费次数
						if(c.productNum ==4) {
    //产品数为零,开始消费
							System.out.println("产品数不为零,开始消费");
							while (c.productNum>0) {
    
								c.productNum--;//消费产品
								System.out.println("库存:="+c.productNum);
							}
							System.out.println("产品数为:"+c.productNum + ",结束消费");
							c.notify();//唤醒生产者线程
						}else {
    
							try {
    
								c.wait();//消费者线程等待
							} catch (InterruptedException e) {
    
								e.printStackTrace();
							}
						}
					}
				}
			}
		},"消费者").start();;
	}
}

class Clerk{
    //商家
	
	public static int productNum = 0;
	
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_44667083/article/details/107580053

智能推荐

随便推点