Java极速入门系列-第六章 Java多线程、合集框架(Collection 接口、泛型)

1.多线程

1.1线程和进程

进程:计算机正在运行的一个独立的应用程序

线程:是组成进程的基本单位

一个进程由一个或多个线程组成

进程是独享内存空间,线程是共享内存空间(线程执行是相互独立的)

单独的线程无法执行,必须依赖于进程才能执行

多线程:在一个进程中,多个线程同时执行,其实是所有线程轮流交替占用CPU资源,中间过程较短,所以说多个线程同时执行

main方法就是一个单线程,所以先输出循环Test,再输出循环Test2

在这里插入图片描述

线程是用来执行任务的,每个线程对象都需要绑定一个任务

Java 中线程是对象,任务也是对象

1.2Java中线程的使用

继承Thread类 ,实现Runnable接口

1.继承Thread类

在这里插入图片描述

在这里插入图片描述

两个线程交替占用cpu运行

继承Thread类将任务和线程绑定了,这个线程就只能做这种事,解耦合度高

2.实现Runnable接口

Runnable 接口就是用来描述任务的,实现 run 方法来描述任务

实现线程和任务的解耦合,单独定义一个类实现 Runnable接口作为任务,创建 Thread 对象之后再把任务整合进去

定义任务

package test;
public class MyRunnable implements Runnable {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 100; i++) {
   
            System.out.println("===Runnable===");
        }
    }
}

创造线程,执行任务

package test;
public class Test {
   
    public static void main(String[] args) {
   
		//创建任务
        MyRunnable myRunnable = new MyRunnable();
		//创建线程
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

1.3线程的状态

  • 创建状态 相当于(Thread thread = new Thread(myRunnable);)
  • 就绪状态 相当于(thread.start();)
  • 运行状态 (交替执行时 ,正在执行的线程就属于运行状态)
  • 阻塞状态 (交替执行时 ,其他线程占用cpu执行时,此时此线程属于阻塞状态)
  • 终止状态
    在这里插入图片描述

1.4线程调度

1.线程休眠

让当前线程暂停执行,运行状态->阻塞状态,让出 CPU 资源,sleep(long millis) 单位毫秒,创建出的线程都可以调用sleep,主线程不行

package test;
public class MyThread extends Thread {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 100; i++) {
   
            if(i==5){
   
                try {
   
                    sleep(1000);
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }
            System.out.println("---MyThread---");
        }
    }
}
//外部做休眠
package test;
public class Test {
   
    public static void main(String[] args) {
   
        MyThread myThread = new MyThread();
        try {
   
            myThread.sleep(1000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        myThread.start();
        for (int i = 0; i < 100; i++) {
   
            System.out.println("---main---");
        }
    }
}

主线程调用sleep方法 Thread.currentThread().sleep(1000);

package test;
public class Test {
   
    public static void main(String[] args) {
   
        for (int i = 0; i < 100; i++) {
   
            if(i==5){
   
                try {
   
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException
                        e) {
   
                    e.printStackTrace();
                }
            }
            System.out.println("---main---");
        }
    }
}

2.线程合并

将指定的某个线程加入到当前线程中,合并为一个线程。join()

A 和 B,B.join() 表示当前开始 CPU 资源被 B 独占,A 进入阻塞状态,B 执行完毕,A 才能继续继续执行。

package test;
public class JoinRunnable implements Runnable {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 100; i++) {
   
            System.out.println(i + "------JoinRunnable");
        }
    }
}

package test;
public class Test {
   
    public static void main(String[] args) {
   
        JoinRunnable joinRunnable = new JoinRunnable();
        Thread thread = new Thread(joinRunnable);
        thread.start();
        for (int i = 0; i < 100; i++) {
   
            if(i == 10){
   
                try {
   
                    thread.join();
                } catch (InterruptedException
                        e) {
   
                    e.printStackTrace();
                }
            }
            System.out.println(i + "======main");
        }
    }
}

当运行到9时,从10开始都是执行joinRunnable,知道joinRunnable执行完成

join有个方法重写

join(long millis)

B.join() 表示当前开始 CPU 资源被 B 独占,A 进入阻塞状态,在 millis 时间内 B 独占 CPU 资源进行执行,但是时间一到无论 B 是否执行完毕,都会释放 CPU 资源,和 A 继续交替执行。

package test;
public class JoinRunnable implements Runnable {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 20; i++) {
   try {
   
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
            System.out.println(i + "------ JoinRunnable");
        }
    }
}
package test;
public class Test {
   
    public static void main(String[] args) {
   
        JoinRunnable joinRunnable = new JoinRunnable();
        Thread thread = new Thread(joinRunnable);
        thread.start();
        for (int i = 0; i < 100; i++) {
   
            if(i == 10){
   
                try {
   
                    thread.join(3000);
                } catch (InterruptedException
                        e) {
   
                    e.printStackTrace();
                }
            }
            System.out.println(i + "======main");
        }
    }
}

3.线程礼让

在某个特定的时间点,让线程暂停抢占 CPU 资源的行为,运行状态 -> 阻塞状态,yield(),当这个礼让之后继续抢占资源

线程1

package test;
public class YieldThread1 extends Thread {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 10; i++) {
   
            if(i==5){
   
                Thread.currentThread().yield();
            }
            System.out.println(Thread.currentThread().getN
                    ame() + "------" + i);
        }
    }
}

线程2

package test;
public class YieldThread2 extends Thread {
   
	@Override
	public void run() {
   
		for (int i = 0; i < 10; i++) {
   
System.out.println(Thread.currentThread().getName() + "------" + i);
}
}
}

运行

package test;
public class Test {
   
    public static void main(String[] args) {
   
        YieldThread1 thread1 = new YieldThread1();
        thread1.setName("线程A");
        YieldThread2 thread2 = new YieldThread2();
        thread2.setName("线程B");
        thread1.start();
        thread2.start();
    }
}

结果,在id=5 ,A礼让了B
在这里插入图片描述

1.5线程同步

多个线程同时访问某个资源时,不是同时对资源进行访问修改,而是顺序执行。

多个线程同时访问共享资源时,可能会存在数据错误的情况,如何解决?

通过线程同步,多个线程不是同时访问数据,而是顺序访问。

1.synchronized 修饰实例方法

//当上一个线程走完时,才会走下一个线程
package test;
public class Account implements Runnable {
   
    private static int num;
    @Override
    public synchronized void run() {
   
        try {
   
            Thread.currentThread().sleep(1);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        num++;
        System.out.println(Thread.currentThread().getN
                ame() + "是当前的第" + num + "位访客");
    }
}
package test;
public class Test {
   
    public static void main(String[] args) {
   
        Account account = new Account();
        Thread thread1 = new Thread(account,"线 程A");
                Thread thread2 = new Thread(account,"线程B");
                thread1.start();
        thread2.start();
    }
}

2.synchronized 修饰静态方法

package test;
public class Test2 {
   
    public static void main(String[] args) {
   
        for (int i = 0; i < 5; i++) {
   
            Thread thread = new Thread(new Runnable(){
   
                @Override
                public void run() {
   
                    Test2.test();
                }
            });
            thread.start();
        }
    }
    p
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java泛型Java 5引入的新特性,可以提高代码的可读性和安全性,降低代码的耦合度。泛型是将类型参数化,实现代码的通用性。 一、泛型的基本语法 在声明类、接口、方法时可以使用泛型泛型的声明方式为在类名、接口名、方法名后面加上尖括号<>,括号中可以声明一个或多个类型参数,多个类型参数之间用逗号隔开。例如: ```java public class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T> { T getData(); void setData(T data); } public <T> void genericMethod(T data) { System.out.println(data); } ``` 其中,`GenericClass`是一个泛型类,`GenericInterface`是一个泛型接口,`genericMethod`是一个泛型方法。在这些声明中,`<T>`就是类型参数,可以用任何字母代替。 二、泛型的使用 1. 泛型类的使用 在使用泛型类时,需要在类名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericClass<String> gc = new GenericClass<>(); gc.setData("Hello World"); String data = gc.getData(); ``` 在这个例子中,`GenericClass`被声明为一个泛型类,`<String>`指定了具体的类型参数,即`data`字段的类型为`String`,`gc`对象被创建时没有指定类型参数,因为编译器可以根据上下文自动推断出类型参数为`String`。 2. 泛型接口的使用 在使用泛型接口时,也需要在接口名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericInterface<String> gi = new GenericInterface<String>() { private String data; @Override public String getData() { return data; } @Override public void setData(String data) { this.data = data; } }; gi.setData("Hello World"); String data = gi.getData(); ``` 在这个例子中,`GenericInterface`被声明为一个泛型接口,`<String>`指定了具体的类型参数,匿名内部类实现了该接口,并使用`String`作为类型参数。 3. 泛型方法的使用 在使用泛型方法时,需要在方法名前面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java genericMethod("Hello World"); ``` 在这个例子中,`genericMethod`被声明为一个泛型方法,`<T>`指定了类型参数,`T data`表示一个类型为`T`的参数,调用时可以传入任何类型的参数。 三、泛型的通配符 有时候,我们不知道泛型的具体类型,可以使用通配符`?`。通配符可以作为类型参数出现在方法的参数类型或返回类型中,但不能用于声明泛型类或泛型接口。例如: ```java public void printList(List<?> list) { for (Object obj : list) { System.out.print(obj + " "); } } ``` 在这个例子中,`printList`方法的参数类型为`List<?>`,表示可以接受任何类型的`List`,无论是`List<String>`还是`List<Integer>`都可以。在方法内部,使用`Object`类型来遍历`List`中的元素。 四、泛型的继承 泛型类和泛型接口可以继承或实现其他泛型类或泛型接口,可以使用子类或实现类的类型参数来替换父类或接口的类型参数。例如: ```java public class SubGenericClass<T> extends GenericClass<T> {} public class SubGenericInterface<T> implements GenericInterface<T> { private T data; @Override public T getData() { return data; } @Override public void setData(T data) { this.data = data; } } ``` 在这个例子中,`SubGenericClass`继承了`GenericClass`,并使用了相同的类型参数`T`,`SubGenericInterface`实现了`GenericInterface`,也使用了相同的类型参数`T`。 五、泛型的限定 有时候,我们需要对泛型的类型参数进行限定,使其只能是某个类或接口的子类或实现类。可以使用`extends`关键字来限定类型参数的上限,或使用`super`关键字来限定类型参数的下限。例如: ```java public class GenericClass<T extends Number> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T extends Comparable<T>> { T getData(); void setData(T data); } ``` 在这个例子中,`GenericClass`的类型参数`T`被限定为`Number`的子类,`GenericInterface`的类型参数`T`被限定为实现了`Comparable`接口的类。 六、泛型的擦除 在Java中,泛型信息只存在于代码编译阶段,在编译后的字节码中会被擦除。在运行时,无法获取泛型的具体类型。例如: ```java public void genericMethod(List<String> list) { System.out.println(list.getClass()); } ``` 在这个例子中,`list`的类型为`List<String>`,但是在运行时,`getClass`返回的类型为`java.util.ArrayList`,因为泛型信息已经被擦除了。 七、泛型的类型推断 在Java 7中,引入了钻石操作符<>,可以使用它来省略类型参数的声明。例如: ```java List<String> list = new ArrayList<>(); ``` 在这个例子中,`ArrayList`的类型参数可以被编译器自动推断为`String`。 八、总结 Java泛型是一个强大的特性,可以提高代码的可读性和安全性,降低代码的耦合度。在使用泛型时,需要注意它的基本语法、使用方法、通配符、继承、限定、擦除和类型推断等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你我有猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值