文章目录
进程与线程的简单理解
程序是指令的集合,程序跑起来就是进程。
同一个程序可以有多个进程,比如qq可以启动多个客户端程序登陆不同的账号。
进程是资源分配的最小单位。线程是cpu调度的最小单位。
进程之间不会共享资源,否则一个软件就会获取到另一个软件的密码,而线程之间可以共享内存等资源。
开启多线程的三个方式
Thread
- 通过继承Thread类来获取多线程能力
- 该类实现了Runnable接口,具有start方法
- 不建议使用,存在OOP单继承的局限性
public class thread_test extends Thread{
//使用同一个资源时还是会冲突
//当一个线程在输出,还没来得及i--时,另一个线程执行了输出
static int i=20;
@Override
public void run() {
while (i>0){
System.out.println(Thread.currentThread().getName()+": "+i--);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
thread_test t1 = new thread_test();
thread_test t2 = new thread_test();
thread_test t3 = new thread_test();
t1.start();
t2.start();
t3.start();
}
}
Runnable
- 实现Runnable接口
- 需要将接口实现传递给new Thread()来获取多线程能力,用到了静态代理的方式
- 可以建一个对象传递给多个new Thread来构建多个线程
- 可以避免单继承局限性,推荐使用
public class Runnable_test implements Runnable{
static int i = 20;
@Override
public void run() {
while (i>0){
System.out.println(Thread.currentThread().getName()+": "+i--);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Runnable_test r1 = new Runnable_test();
// Runnable_test r2 = new Runnable_test();
// Runnable_test r3 = new Runnable_test();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r1);
Thread t3 = new Thread(r1);
t1.start();
t2.start();
t3.start();
}
}
Collable
- 可以有返回值
public class Callable_Test implements Callable<Boolean> {
static int i = 20;
@Override
public Boolean call() throws Exception {
while (i>0){
System.out.println(Thread.currentThread().getName()+" : "+i--);
Thread.sleep(20);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable_Test ct1 = new Callable_Test();
Callable_Test ct2 = new Callable_Test();
//创建线程池
ExecutorService es = Executors.newFixedThreadPool(3);
Future<Boolean> s1 = es.submit(ct1);
Future<Boolean> s2 = es.submit(ct2);
Boolean b1 = s1.get();
Boolean b2 = s2.get();
es.shutdown();
}
}
关于Java泛型
- 由于Collable接口使用到了泛型,我看了下面的博客文章,觉得写得比较仔细,先记录在这里,下次用到比较多的时候再深入学习。
- https://www.cnblogs.com/jingmoxukong/p/12049160.html
- 若使用Object作为参数类型,虽然也可以传入不同的对象,但是生成的对象相当于:Object o = new A(),只能使用object中已有的方法;但是若使用泛型,则相当于:A a = new A(),使用起来更为灵活
静态代理
- 上面Runnable实现多线程采用了静态代理的方式,具体参考下面的笔记
- https://www.cnblogs.com/cC-Zhou/p/9525638.html
代理类和被代理类必须实现同一个接口或者继承同一个类,重写了同一个方法
当要调用被代理类的方法时,可以通过调用代理类的同名方法实现
通过代理类来调用被代理类的方法,通常会在调用方法的前后进行一个功能的附加和增强
lambda表达式
- 只有一个抽象方法的接口为函数式接口,这种接口的实现类可以用lambda表达式表示
public class InnerClassTest {
public static void main(String[] args) {
saticInner saticInner = new saticInner();
saticInner.run();
//3.局部内部类,定义在方法内部的类,其对象创建要在类定义的后面
class partInner implements innerRun{
@Override
public void run() {
System.out.println("part inner class test");
}
}
partInner partInner = new partInner();
partInner.run();
//4.匿名内部类,在创建对象的同时重写方法,没有名字
innerRun r = new innerRun() {
@Override
public void run() {
System.out.println("anonymous class");
}
};
r.run();
//5.lambda表达式。
//因为只有一个方法,直接在花括号内写重写方法的表达式即可
//innerRun lanbdaTest = () -> {System.out.println("lambda 1");};
//只有一个表达式的情况下可以去掉花括号
//innerRun lanbdaTest = () -> System.out.println("lambda 1");
//使用参数的情况下
//lambadTest lanbdaTest = (int a) -> {System.out.println(a);};
//一个参数的情况下可以去掉括号,多个参数的情况下不行
//可以同时去掉所有参数的类型
lambadTest lanbdaTest = a -> {System.out.println("lambda 1");};
}
//2.静态内部类,定义在类内部的静态类
static class saticInner implements innerRun{
@Override
public void run() {
System.out.println("static inner class test");
}
}
}
//1.普通实现类
class normalClass implements innerRun{
@Override
public void run() {
System.out.println("normal class test");
}
}
interface innerRun{
void run();
}
interface lambadTest{
void run0(int a);
}
停止线程的方式
- 一般不推荐通过thread自带的stop,destory等方法来停止线程,已废弃。
- 推荐通过设置标志位停止
public class stopThread implements Runnable{
//结束标志
boolean flag=true;
int i = 0;
@Override
public void run() {
//设置一个开始条件
while (flag){
System.out.println(i++);
}
}
public void stop(){
flag=false;
}
public static void main(String[] args) {
stopThread st = new stopThread();
Thread thread = new Thread(st);
thread.start();
for (int j =0 ;j<1000;j++){
System.out.println("main"+j);
}
//通过这个方法使得当前对象的flag为false,从而停止线程运行
st.stop();
}
}
sleep–线程休眠
- 通过thread.sleep()方法可以模拟网络延迟
- 还可以进行倒计时
public class sleepTestr {
public static void main(String[] args) {
countDown();
}
public static void countDown(){
for (int i = 10; i > 0; i--) {
Date date = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
yield–线程礼让
- 礼让是让线程从运行到就绪,让CPU重新分配,但不一定成功,因为还是要看cpu的分配
public class yieldTest implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start");
//Thread.yield();
System.out.println(Thread.currentThread().getName()+" stop");
}
public static void main(String[] args) {
Thread t1 = new Thread(new yieldTest());
Thread t2 = new Thread(new yieldTest());
t1.start();
t2.start();
}
}
join–线程强行执行
- 让join的线程先执行
public class joinTest implements Runnable{
@Override
public void run() {
for (int i = 100; i > 0; i--) {
System.out.println(Thread.currentThread().getName()+i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new joinTest());
t1.start();
for (int i = 200; i > 0; i--) {
if (i==100){
t1.join();
}
System.out.println("main"+i);
}
}
}
线程状态
- thread.getState:获取线程的状态
线程优先级
- 主线程的优先级是默认的
- 必须先设置优先级,再启动线程
- 线程优先级最大是10,最小是1,超过范围会报错
- 默认值都是5,意味着获取调度的概率都相同
- 优先级高不一定会优先执行,只能说优先的概率比较高
public class priorityTest implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": "+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getPriority());
Thread t1 = new Thread(new priorityTest(), "t1");
t1.setPriority(1);
//设为守护线程
t1.setDaemon(true);
t1.start();
Thread t2 = new Thread(new priorityTest(), "t2");
t2.setPriority(7);
t2.start();
Thread t3 = new Thread(new priorityTest(), "t3");
t3.setPriority(Thread.MAX_PRIORITY);
t3.start();
}
}
守护线程
- JVM虚拟机必须保证用户线程执行完毕才会退出运行,但是不必等待守护线程执行完毕
- 后台记录操作日志,监控内存,垃圾回收等都是守护线程
- thread.seydaemon(true)表示设为守护线程,默认值为false,意味着即使该线程没有运行完也JVM也可以退出