Java注解也就是Annotation,是Java里的特殊标记,它为Java程序代码提供了一种形式化的方法,用来表达代码本身无法表示的额外的信息。语法:@Annotation
使用注解语法有以下几点规范:
将注解置于所有修饰符之前。
通常将注解单独放置在一行。
默认情况下,注解可用于修饰任意程序元素,包括类、方法和成员变量等。
根据使用方法和用途,可将注解分为三类:内建注解、元注解和自定义注解。
@Override、@Deprecated以及@SuppressWarnings
@Override注解用来标注方法,表示该方法时重写父类的某方法。
public interface Person {
void Override();
}
public class Student implements Person{
@Override
public void Override() {
}
}
@Deprecated注解标识程序元素已过时,如果程序元素被此注解修饰,编译器将不再鼓励使用这个被标注的程序元素,如果使用,编译器会在程序元素画一条斜线。下面的代码如果没有显示斜线效果,可以手打到程序中试一试。
@Deprecated
public Student(int age, String name) {
this.age = age;
this.name = name;
}
Student stu=new Student(18,"张三");
@SuppressWarnings注解标识阻止编译器警告,被用于有选择地关闭编译器对类、方法和成员变量等程序元素及其子元素的警告。
@SuppressWarnings参数:
deprecation:使用了过时的程序元素。
unchecked:执行了未检查的转换。
unused:有程序元素未被使用。
fallthrough:switch程序块直接通往下一种情况而没有break。
path:在类路径、源文件路径等中有不存在的路径。
serial:在可序列化的类上缺少serialVersionUID定义。
finally:任何finally子句不能正常完成。
all:所有情况。
举个简单的例子,比如在程序中输入 int a = 0 ;鼠标放到a上,会显示未被使用,加此注解之后就不会提示了。
@SuppressWarnings("unused")
int a=0;
@Target注解、@Retention注解、@Documented注解、@Inherited注解
@Target注解用于指定被其修饰的注解能用于修饰哪些程序元素,此注解类型有唯一的value作为成员变量。变量是ElementType类型,是可以被标注的程序元素的枚举类型。
ElementType.ANNOTATION_TYPE:注解声明。
ElementType.CONSTRYCTOR:构造方法声明。
ElementType.FIELD:成员变量声明。
ElementType.LOCAL_VARIABLE:局部变量声明。
ElementType.METHOD:方法声明。
ElementType.PACKAGE:包声明。
ElementType.PARAMETER:参数声明。
ElementType.TYPE:类、接口或枚举声明。
@Retention注解描述了被其修饰的注解是否被编译器丢弃或者保留在class文件中。默认情况下,注解被保存在class文件中,但在运行时并不能被反射访问。此注解包含了一个RetentionPolicy类型的value成员变量,有如下三个枚举类型值。
@RetentionPolicy.CLASS:@Retention注解中value成员变量的默认值,表示编译器会把被修饰的注解记录在class文件中,但当Java程序运行时,Java虚拟机不再保留注解,从而无法通过反射对注解进行访问。
@RetentionPolicy.RUNTIME:表示编译器将注解记录在class文件中,在运行程序时,Java虚拟机会保留注解,程序可以通过反射获取该注解。
@RetentionPolicy.SOURCE:表示编译器将直接丢弃被修饰的注解。
@Documented注解用于指定被其修饰的注解将被JavaDoc工具提取成文档,此注解没有成员变量。
@Inherited注解用于指定被其修饰的注解将具有继承性。
注解类型是一种接口,但是又不同于接口,定义一个新的注解类型与定义一个接口非常相似,使用@interface关键字,自定义注解在实际的开发中使用的频率并不是很多,我也就不多讲了。说实话,我也不会啊~
顺带一提,通过反射方法读取注解信息可以去查查API。
首先讲讲什么是进程,进程是程序的一次动态执行过程,是系统运行程序的基本单位,每一个进程都有自己独立的一块内存空间、一组系统资源。每一个进程的内部数据和状态都是完全独立的。
而线程是进程中执行运算的最小单位,一个进程在其执行过程中可以产生多个线程,而线程必须在某个进程内执行。多线程就是在一个进程中同时运行了多个线程,用来完成不同的工作。根据处理级别的不同,线程也分为核心级线程和用户级线程。
核心级线程是和系统任务相关的线程,负责处理不同进程之间的多个线程。
用户级线程就是在开发程序时,由于程序的需要而编写的线程。
一个进程中至少要有一个线程。
资源分配给进程,同一进程的所有线程共享该进程的所有资源。
处理机分配给进程,即真正在处理机上运行的是线程。
多线程程序可以带来更好的用户体验,避免因程序执行过慢而导致出现计算机死机或者白屏的情况。
多线程程序可以最大限度的提高计算机系统的利用效率,如迅雷的多线程下载。
每个程序至少自动拥有一个线程,称为主线程,Java程序中main方法即为主线程的入口。定义一个县城类通常有两种方法,分别是继承Thread类和实现Runnable接口。
继承Thread类:
public class MyThread extends Thread{
private int count=0;
public void run(){
while(count<100){
count++;
System.out.println("count的值是:"+count);
}
}
public static void main(String[] args) {
MyThread mt=new MyThread();
mt.start();
}
}
实现Runnable接口:
public class MyThread1 implements Runnable{
private int count=0;
@Override
public void run() {
while(count<100){
count++;
System.out.println("count的值是:"+count);
}
}
public static void main(String[] args) {
Thread thread=new Thread(new MyThread1());
thread.start();
}
}
上述的两段简单代码是创建并启动了一个线程,启动线程要使用start方法,如果使用run()方法,则表示运用的主线程。
两种方法都可以用来创建一个新的线程,但是在实际开发应用中,第二种方法用的比较多,因为Java是单根继承,只能有一个直接父类,但是却可以实现多个接口。
线程的状态:五个状态,创建,就绪,运行,阻塞,死亡。有的地方叫法不一样,不过都一个意思。
线程调度中的简单方法就不一一赘述了,讲一下优先级,每个线程创建时优先级都是5,优先级由1~10依次递增,10表示优先级最高,利用setPriority()方法可更改线程的优先级。括号内要填入1~10的整数。
线程的同步问题比较棘手,我尽量详细。
当两个或多个线程需要访问同一资源时,需要以某种顺序来确保该资源在某一时刻只能被一个线程使用的方式称之为线程同步。实现线程的同步通过synchronized关键字实现,添加关键字的方式称之为同步方法和同步代码块。通过以下两个例子可以看出线程同步的作用。
未使用synchronized:
public class Account {
private int balance=500;
public int getBalance(){
return balance;
}
public void withdraw(int amount){
balance=balance-amount;
}
public static void main(String[] args) {
TestAccount ta=new TestAccount();
Thread one=new Thread(ta);
Thread two=new Thread(ta);
one.setName("张三");
two.setName("张三老婆");
one.start();
two.start();
}
}
class TestAccount implements Runnable{
private Account acct=new Account();
@Override
public void run() {
for (int i = 0; i <5 ; i++) {
makeWithdrawal(100);
if(acct.getBalance()<0){
System.out.println("账户透支了");
}
}
}
private void makeWithdrawal(int amt){
if(acct.getBalance()>=amt){
System.out.println(Thread.currentThread().getName()+"准备取款");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
acct.withdraw(amt);
System.out.println(Thread.currentThread().getName()+"完成取款");
}else{
System.out.println("余额不足支付"+Thread.currentThread().getName()+"的取款,余额为"+acct.getBalance());
}
}
}
使用synchronized
public class Account1 {
private int balance=500;
public int getBalance(){
return balance;
}
public void withdraw(int amount){
balance=balance-amount;
}
public static void main(String[] args) {
TestAccount1 ta=new TestAccount1();
Thread one=new Thread(ta);
Thread two=new Thread(ta);
one.setName("张三");
two.setName("张三老婆");
one.start();
two.start();
}
}
class TestAccount1 implements Runnable{
private Account1 acct=new Account1();
@Override
public void run() {
for (int i = 0; i <5 ; i++) {
makeWithdrawal(100);
if(acct.getBalance()<0){
System.out.println("账户透支了");
}
}
}
synchronized private void makeWithdrawal(int amt){
if(acct.getBalance()>=amt){
System.out.println(Thread.currentThread().getName()+"准备取款");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
acct.withdraw(amt);
System.out.println(Thread.currentThread().getName()+"完成取款");
}else{
System.out.println("余额不足支付"+Thread.currentThread().getName()+"的取款,余额为"+acct.getBalance());
}
}
}
出现死锁的情况:如果多个线程都处于等待状态而无法唤醒时,就构成了死锁。两个或者两个以上的线程在活动,某线程在拿到一个锁后还想拿第二个锁,造成锁的嵌套。
如何避免死锁:线程因某个条件未满足而受阻,不能让其继续占有资源,如果多个对象需要互斥访问,应确定线程获得锁的顺序,并保证整个程序以相反的顺序释放锁。尽量减少同步方法或者同步代码块的嵌套。
讲讲概念,然后通过代码理解吧
Java提供了三个方法实现线程之间的通信:
wait():挂起当前线程,释放共享资源的锁。
notify():调用任意对象的notify()方法会在因调用该对象的wait()方法而阻塞的线程中随机选择一个线程解除阻塞,但是需要获得锁才可真正执行。
Notifyall():调用了notifyAll()方法会将因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。
public class SharedData {
private char c;
private boolean isProduced = false;
synchronized public void putShareChar(char c) {
if (isProduced) {
try {
System.out.println("消费者还未消费,生产者停止生产");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.c = c;
isProduced = true;
notify();
switch (c){
case 'A':
System.out.println("生产了商品--M416--,通知消费者消费");
break;
case 'B':
System.out.println("生产了商品--AK47--,通知消费者消费");
break;
case 'C':
System.out.println("生产了商品--98K--,通知消费者消费");
break;
case 'D':
System.out.println("生产了商品--三级头--,通知消费者消费");
break;
case 'E':
System.out.println("生产了商品--平底锅--,通知消费者消费");
break;
case 'F':
System.out.println("生产了商品--AWM--,通知消费者消费");
break;
case 'G':
System.out.println("生产了商品--三级甲--,通知消费者消费");
break;
default:
System.out.println("程序员自行制作外挂");
}
}
synchronized public char getShareChar() {
if (!isProduced) {
try {
System.out.println("生产者还未生产,因此消费者不能消费");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isProduced=false;
notify();
switch (c){
case 'A':
System.out.println("消费者购买了商品--M416--压枪稳的一批");
break;
case 'B':
System.out.println("消费者购买了商品--AK47--点射狠的一批");
break;
case 'C':
System.out.println("消费者购买了商品--98K--爆头嗨的一批");
break;
case 'D':
System.out.println("消费者购买了商品--三级头--头盖骨硬的一批");
break;
case 'E':
System.out.println("消费者购买了商品--平底锅--挡子弹爽的一批");
break;
case 'F':
System.out.println("消费者购买了商品--AWM--偷人嗨的一批");
break;
case 'G':
System.out.println("消费者购买了商品--三级甲--皮糙肉厚的一批");
break;
default:
System.out.println("无敌BUFF,血腥的一批");
}
return this.c;
}
public static void main(String[] args) {
SharedData s=new SharedData();
new Consumer(s).start();
new Producer(s).start();
}
}
class Producer extends Thread{
private SharedData s;
Producer(SharedData s){
this.s=s;
}
public void run(){
for(char ch='A';ch<='H';ch++){
try {
Thread.sleep((int)(Math.random()*3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
s.putShareChar(ch);
}
}
}
class Consumer extends Thread{
private SharedData s;
Consumer(SharedData s){
this.s=s;
}
public void run(){
char ch;
do{
try {
Thread.sleep((int)(Math.random()*3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
ch=s.getShareChar();
}while(ch!='H');
}
}
线程这一块主要是思路要清晰,所以理解概念和理解方法都比较简单,通过练习的方法可以增加掌握。
练习:
0.在Java应用程序中,使用两个线程分别输出100以内的奇数和偶数,并按从小到大的顺序输出。
public class First {
boolean flag=true;
int i=0;
}
class Jishu implements Runnable{
First num;
public Jishu(First num){
this.num=num;
}
@Override
public void run() {
while(num.i<=100){
synchronized (num){
if(num.flag){
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("奇数AAA为"+num.i);
num.i++;
num.flag=true;
num.notify();
}
}
}
}
}
class Oushu implements Runnable{
First num;
public Oushu(First num){
this.num=num;
}
@Override
public void run() {
while(num.i<=100) {
synchronized (num) {
if (!num.flag) {
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("偶数BBB为" + num.i);
num.i++;
num.flag = false;
num.notify();
}
}
}
}
public static void main(String[] args) {
First first=new First();
Thread t1=new Thread(new Jishu(first));
Thread t2=new Thread(new Oushu(first));
t1.start();
t2.start();
}
}
1.使用继承Thread和实现Runable接口的两种方法来创建两个线程,并给这两个线程命名,分别打印出1~10 的数字,要求打印的时候带上当前的线程名字。
public class FirstThread extends Thread{
public void run(){
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"-"+i);
}
}
}
Thread t1=new Thread(new FirstThread());
Thread t2=new Thread(new FirstThread());
t1.setName("线程A");
t2.setName("线程B");
t1.start();
t2.start();
public class FirstRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
}
Thread t1=new Thread(new FirstRunnable()) ;
Thread t2=new Thread(new FirstRunnable());
t1.setName("线程A");
t2.setName("线程B");
t1.start();
t2.start();
2.
使用sleep
的方法实现线程休眠,实现使用多线程创建两个线程,并给这两个线程命名,分别打印出
1~10
的数字,要求打印的时候带上当前的线程名字。
public class SecondRunnable implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thread t1=new Thread(new SecondRunnable()) ;
Thread t2=new Thread(new SecondRunnable());
t1.setName("线程A");
t2.setName("线程B");
t1.start();
t2.start();
3.
使用join
的方法实现使用多线程创建两个线程
(
其中一个是主线程
)
,并给这两个线程命名,分别打印出
1~10
的数字,要求主线程打印到
3
的时候使用
join
方法让子线程先打印完成,打印的时候带上当前的线程名字。
public class ThirdRunnable implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thread t1=new Thread(new ThirdRunnable());
t1.setName("线程A");
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(i==3){
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.使用多线程的方式设计一个抢票软件,参加抢票的有携程,途牛和飞猪,要求做到线程安全,打印出抢票人,抢到第几张票,剩余票数。
public class FourthRunnable implements Runnable{
private int count=10;
private int num=0;
private boolean flag=false;
@Override
public void run() {
while(!flag){
sale();
}
}
public synchronized void sale(){
if (count<=0){
flag=true;
return;
}
count--;
num++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,还剩"+count+"张票");
}
}
FourthRunnable fr=new FourthRunnable();
Thread t1=new Thread(fr);
Thread t2=new Thread(fr);
Thread t3=new Thread(fr);
t1.setName("携程");
t2.setName("途牛");
t3.setName("飞猪");
t1.start();
t2.start();
t3.start();
5.
使用线程的等待与通知的方法,模拟一个提供餐饮的程序,要求如果厨师有4
道菜没做好,就停止点菜,每做好一道菜,如果没做好的菜少于
4
道,通知服务员可以接受点菜,有人点菜,通知厨师做菜。
public class FifthCooker extends Thread{
private FifthMakeFood s;
FifthCooker(FifthMakeFood s){
this.s=s;
}
public void run(){
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
s.makeFood();
}
}
}
public class FifthCounsumer extends Thread {
private FifthMakeFood s;
FifthCounsumer(FifthMakeFood s) {
this.s = s;
}
public void run() {
for (int i = 1;i<20 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
s.eatFood();
}
}
}
public class FifthMakeFood {
private int count = 0;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
synchronized public void makeFood() {
//做菜
if(count==0){
System.out.println("没有生意,等待接单");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(count>0){
count--;
System.out.println("做出一道菜,通知服务员可以点菜");
notify();
}
}
synchronized public void eatFood() {
//点菜,从点菜开始
if(count==4){
System.out.println("厨师正在做4道菜,不可点菜");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(count<4&&count>=0){
count++;
System.out.println("点菜成功");
notify();
}
}
}
public class FifthTest {
public static void main(String[] args) {
FifthMakeFood fmf=new FifthMakeFood();
new FifthCooker(fmf).start();
new FifthCounsumer(fmf).start();
}
}