多线程笔记

JAVA学习网 2020-09-04 16:43:26

1、线程的状态

狂神说多线程讲解

休眠方法:

public static void sleep(long millis)

放弃方法:放弃对时间片的使用

public static void yield()

优先级方法、守护线程方法:

守护线程的作用:

JVM 程序在什么情况下能够正常退出?

答:当 JVM 中不存在任何一个正在运行的非守护线程时,则 JVM 进程即会退出。

如果 JVM 中没有一个正在运行的非守护线程,这个时候,JVM 会退出。换句话说,守护线程拥有自动结束自己生命周期的特性,而非守护线程不具备这个特点

2、多线程特点以及创建方式

多线程的特点是抢占式
cpu时间片安排线程的访问时间

创建线程的方式

继承Thread方法重写run()方法
1.用this.getId()和this.getName()获取线程的id和名称
2.用Thread.currentThread()获取当前线程  //如果当前线程没有继承Thread类的时候用
3.修改线程名称:调用setName方法;利用构造方法 super(name);
public class ThreadTest extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程方法被调用了");
    }

    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        threadTest.start();
    }
}
第二种创建线程的方法:
继承Runnable接口,重写run方法//这个类是一个run方法的实现类,在main方法中创建Thread线程对象时把该方法类传入Thread中可以创建子线程

可以使用匿名内部类的方法直接进行线程创建
public class TestRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现了run方法");
    }
}

  public static void main(String[] args) {

        Thread thread = new Thread(new TestRunnable());
        thread.start();
    }

实现Callable接口,重写call方法,
- 可以定义返回值
- 可以抛出异常
public class TestCallable implements Callable<Boolean> {
     @Override
     public Boolean call() throws Exception {
         System.out.println("创建成功");
         return true;
     }
 
     public static void main(String[] args) {
         TestCallable callable = new TestCallable();
 
         //创建执行服务
         ExecutorService service = Executors.newFixedThreadPool(1);
         //提交执行
         Future<Boolean> result = service.submit(callable);
 
         boolean isTrue = result.get();
         service.shutdownNow();
 
     }
 
 }

但是,Runnable缺少的一项功能是,当线程终止时(即run()完成时),我们无法使线程返回结果。为了支持此功能,Java中提供了Callable接口。

为了实现Runnable,需要实现不返回任何内容的run()方法,而对于Callable,需要实现在完成时返回结果的call()方法。请注意,不能使用Callable创建线程,只能使用Runnable创建线程。
另一个区别是call()方法可以引发异常,而run()则不能。
为实现Callable而必须重写call方法。

3、如何保证线程安全性

1.

同步代码块
synchronized{

}

注意线程中的循环会一直执行,但是会根据时间片分配的时间一直执行,不会从头开始循环

2.

当使用静态方法时候锁是(类.class)也就是当前类的类型,非静态方法锁是this

3. 死锁

4、线程的同步异步

Lock锁

lock锁和synchronized对比

Lock是显示锁(手动开启和关闭,别忘记关闭锁) synchronized是隐式锁,出了作用域自动释放

Lock只有代码块锁,synchronized有代码块锁和方法锁

使用lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)

优先使用顺序

Lock > 同步代码块 > 同步方法


public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}


class TestLock2 implements Runnable {

    int ticketNums = 10;
    //定义Lock锁
    private ReentrantLock reentrantLock = new ReentrantLock();


    @Override
    public void run() {

        while (true) {
            try {
                reentrantLock.lock(); //加锁
                if(ticketNums > 0 ){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "购买了第" + ticketNums-- + "张票");

                } else {
                    break;
                }
            } finally {
                //解锁
                reentrantLock.unlock();
            }

        }
    }
}

5、读写锁、互斥锁

读写锁的效率比互斥锁高很多

读写锁关系:写和写、写和读互斥、读和读不互斥

互斥锁全部都互斥

3、使用线程池

  • 背景: 经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
  • 思路: 提前创建好多个线程,放入线程池中,使用时直接获取,使用完毕放回池中,可以避免频繁的创建销毁,实现重复利用,类似生活中的工共交通工具
  • 好处
    • 提高了响应速度(减少了创建新线程的时间)
    • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    • 便于线程管理(…)
      • corePoolSize: 核心池的大小
      • maximumPoolSize: 最大线程数
      • keepAliveTime: 线程没有任务时最多保持多长时间后会终止

blic class Main {
    public static void main(String[] args) {
        TV tv = new TV();
        new Watcher(tv).start();
        new Player(tv).start();
    }
}
阅读(2266) 评论(0)