35.内部类
一个类定义在另一个类的里面
public class A {
class B{
}
}
编译之后会生成 A.class和 A$B.class (
外部类$内部类.class)
生成内部类的对象 (首先生成一个外部类的对象,然后.new 内部类)
A a = new A();
A.B b = new A().new B();//内部类的名字是 外部类.内部类
还可以用a来生成内部类的对象
A.B b = a.new B();
内部类可以随意使用外部类所定义的成员变量和成员函数。但并不意味着内部类继承了外部类的成员变量和成员函数
public class A {
int i;
class B{
int j;
int funB(){
int result = i + j;
//相当于int result = A.this.i + this.j;
return result;
}
}
}
匿名内部类
1.定义一个接口A.java
public interface A {
void doSomething();
}
2.B类中成员函数需要A类型的对象作为参数
public class B {
public void fun(A a){
System.out.println("B的fun函数");
a.doSomething();
}
}
3.
new A(){
因为A是接口,不能生成对象。所以紧跟{ }用来实现A接口的 抽象函数(复写A中的抽象方法)
}
public class Test {
public static void main(String args[]){
B b = new B();
b.fun(new A(){
public void doSomething(){
System.out.println("A接口的实现");
}
});
}
}
new A(){ .... } 之后就相当于生成了 实现A接口的匿名类 的对象 (不用再写一个 class implements A来实现)
运行结果
36.Java当中的线程(一) 多进程 多线程
多进程:在操作系统中能(同时)运行多个任务(程序)
多线程:在同一应用程序中有多个顺序流(同时)执行
线程是进程当中的一个程序执行流程
一般来说,一个软件就相当于一个进程,进程下面又有多个线程
创建线程的方法(一)
(一)定义一个线程类,继承Thread(由JDK提供)并重写其中的run()。run()方法被称为线程体。
由于Java只支持单继承,用这种方法定义的类不能再继承其他类。
public class FirstThread extends Thread {
public void run(){
for(int i = 0;i<100;i++){
System.out.println("FirstThread-->"+ i);
}
}
}
注意启动线程用 start() 方法,而不是调用run()方法。如果调用run()就不是启动一个新线程,而是只有一个主线程。
public class Test {
public static void main(String args[]){
//生成线程类的对象
FirstThread ft = new FirstThread();
//启动线程
ft.start();
//不能写成ft.run()。如果调用ft.run()会顺序执行run()代码,执行完后再顺序往下执行
for(int i = 0;i<100;i++){
System.out.println("MainThread-->"+ i);
}
}
}
其中共有3个线程:主函数是一个线程, ft是第二个线程,还有垃圾回收这个线程
运行结果
37.
Java当中的线程(二)
实现线程的方法(二)
(二)提供一个实现接口Runnable的类 作为线程的目标对象,在初始化一个Thread类或者Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体
public class RunnableImpl implements Runnable{
public void run(){
for(int i = 0;i<100;i++){
System.out.println("RunnableImplement-->"+ i);
}
}
}
public class Test {
public static void main(String args[]){
//生成一个Runnable接口实现类的对象
RunnableImpl ri = new RunnableImpl();
//生成一个Thread对象,并将Runnable接口实现类的对象作为参数传递给该Thread对象
Thread t = new Thread(ri);
//通知Thread对象,执行start方法
t.start();
}
}
实际开发中,使用接口实现Runnable 这种方法 ,能不用继承就不用继承,毕竟Java只支持单继承
还可以用匿名内部类来实现,注意到Runnable是一个接口。效果是一样的
Thread m = new Thread(new Runnable(){
public void run(){
for(int i = 0;i<100;i++){
System.out.println("RunnableImplement-->"+ i);
}
}
});
m.start();
线程的简单控制方法
1.Thread.sleep() 线程休眠(在休眠时,线程处于阻塞状态)
Thread.sleep(2000);
//sleep()是静态方法,而且会产生check 异常
//线程休眠2000毫秒。休眠过后,线程不会马上运行,而是会转入就绪状态,抢占CPU,抢占到CPU之后才会//执行。
public class RunnableImpl implements Runnable{
public void run(){
for(int i = 0;i<100;i++){
System.out.println("RunnableImplement-->"+ i);
if(i == 50){
try{
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
2.Thread.yield() 一旦运行到这个代码,会让当前线程让出CPU。
注意:
不是说让出CPU就会让其他线程来执行。比如A线程运行到yield(),A线程就让出CPU,之后会和其他线程再次一起抢占CPU.谁抢到CPU谁就执行,可能还是A再次抢到CPU
3.getPriority() , setPriority() 优先级
线程优先级最大是10,最小是1,默认是5
线程优先级越大,执行概率就越大,(将线程优先级设为最大,并不是一定会执行,只是执行概率大)
Thread t = new Thread(ri);
t.setPriority(Thread.MAX_PRIORITY);//设置最大优先级
// t.setPriority(Thread.MIN_PRIORITY);//最小优先级
System.out.println(t.getPriority());
38.Java当中的线程(三)
每一个线程都有名字,可以通过Thread对象的setName()方法设置线程名字,也可以使用getName()方法获取线程的名字
public class Test {
public static void main(String args[]){
MyThread mt = new MyThread();
//两个Thread对象,但是这两个Thread对象共用同一个线程体
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
t1.setName("a");
t2.setName("b");
//分别启动两个线程
t1.start();
t2.start();
}
}
数据安全问题
public class MyThread implements Runnable {
int i = 100;
public void run(){
while(true){
//Thread.currentThread() //Thread中的静态方法,返回当前正在执行这段代码的线程
System.out.println(Thread.currentThread().getName() + i);
i--;
Thread.yield();
if(i<0){
break;
}
}
}
}
使用同步代码块 synchronized(this){ } this指mt这
个MyThread 类的对象
public class MyThread implements Runnable {
int i = 100;
public void run(){
while(true){
synchronized(this){
System.out.println(Thread.currentThread().getName() + i);
i--;
Thread.yield();
if(i<0){
break;
}
}
}
}
}
39.深入同步语法 synchronized
1.Service.java 包含了fun1() , fun2()。都使用了同步代码块synchronized(this){ .. }
public class Service {
public void fun1(){
synchronized(this){
try{
Thread.sleep(3*1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("fun1");
}
}
public void fun2(){
synchronized(this){
System.out.println("fun2");
}
}
}
2.
MyThread1 ,
Mythread2 调用fun1(),fun2()
public class MyThread1 implements Runnable {
private Service service;
public MyThread1(Service service){
this.service = service;
}
public void run(){
service.fun1();
}
}
public class Mythread2 implements Runnable{
private Service service;
public Mythread2(Service service){
this.service = service;
}
public void run(){
service.fun2();
}
}
3.Test.java
public class Test {
public static void main(String args[]){
//整个应用程序中只有一个service对象
Service service = new Service();
Thread t1 = new Thread(new MyThread1(service) );
Thread t2 = new Thread(new Mythread2(service) );
t1.start();
t2.start();
}
}
运行结果
结果分析:
首先启动了t1.start() , 执行fun1()这个方法。fun1()中的synchronized (this),this指的是调用fun1()的这个对象,即在主函数中生成的service
Service service = new Service();
t1拿到这个对象锁之后开始休眠,t1休眠可能使t2开始执行。t2执行fun2(),但由于fun2()中synchronized (this)还是使用的service做锁
Thread t1 = new Thread(new MyThread1(service) );
Thread t2 = new Thread(new Mythread2(service) );
现在t1持有这个锁,t2就无法执行
由于锁住了对象service,所以导致t2也无法执行。只有等t1释放锁之后,t2才会执行。
同步锁锁住的是代码块 这个说法错误。
一旦某一线程获得一个对象的同步锁,那么这个对象上所有被同步的代码 其他的线程都不能执行,都需要等待同步锁被释放之后才能执行。但是不会影响非同步的代码
public void fun2(){
//synchronized(this){
System.out.println("fun2");
//}
}
注释掉fun2()中同步代码后
运行结果为
同步方法 在返回值前面加上synchronized
同步方法锁住的是this (调用这个函数的对象) ,跟同步代码块类似,只不过同步代码块可以指定锁住的是哪一个对象,而同步方法锁住的只能是this
public class Service {
public synchronized void fun1(){
try{
Thread.sleep(3*1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("fun1");
}
public synchronized void fun2(){
//synchronized(this){
System.out.println("fun2");
//}
}
}