Java 多线程小结

Scroll Down

Java 多线程

三种创建方式

  1. 继承 Thread 类
  2. 实现 Runnable 接口
  3. 实现 Callable 接口

继承Thread

public class TestThread extends Thread{
    public void run()
    {
        //run方法线程体
        ...
    }
    
    //调用
    public static void main(String[] args)
    {
        // 开始这个线程
        new TestThread().start();
    }
}

实现 Runnable 接口

public class MyRunnable implements Runnable{
    public void run()
    {
        //run方法线程体
        ...
    }
    
    public static void main(String[] args)
    {
        // 开始这个线程
        new Thread(new MyRunnable()).start();
    }
    
}

实现Callable接口

public class TestCallable implements Callable<Boolean> {
    public Boolean call()
    {
        // 方法体
    }
    public static void main(String[] args)
    {
        ExecetorService service = Executors.newFixedThreadPool();
        
        Future<Boolean> r1 = service.submit(new TestCallable());
       	//此处有异常要抛出
        boolean result = r1.get();
        
        ser.shutdownNow();
    }
}

线程方法

  1. setPriority(int newPriority) 更改线程的优先级
  2. sleep(long millis) 让该进程休眠
  3. join() 等待该线程终止
  4. yield() 暂停当前执行的线程对象
  5. interrupt() 中断线程
  6. isAlive() 测试线程是否处于活动状态

停止线程的方法

使用标志位

private boolean flag = true;
@Override
public void run() {
    int i = 0;
    while (flag)
    {
        System.out.println("thread"+i);
        i++;
    }
}
//外部调用这个方法实现线程的停止
public void stop()
{
    this.flag = false;
}

public static void main(String[] args) {
    TestThread testThread = new TestThread();
    Thread thread = new Thread(testThread);
    thread.start();
    for (int i=0;i<1000;i++)
    {
        System.out.println("main:"+i);
        if (i==900)
            testThread.stop();
    }
}

线程的礼让

public static void main(String[] args) {
    TestThread testThread = new TestThread();
    Thread thread = new Thread(testThread);
    thread.setPriority(10);
    thread.start();
    for (int i=0;i<10000;i++)
    {
        System.out.println("main:"+i);
        if (i%100==0)
            Thread.currentThread().yield();
        if (i==9000)
            testThread.stop();
    }
}

礼让是不一定成功的,是重新竞争的

合并线程 Join

等待此线程执行完在执行其他线程

线程的状态

thread.getState()

线程一旦进入死亡状态就不能再次启动了

守护线程

daemon

  1. 线程分为用户线程和守护线程
  2. 虚拟机必须确保用户线程执行完毕
  3. 虚拟机不用等待守护线程执行完毕
thread.setDaemon(true);		// 代表这个线程是一个守护线程
//如果是 false 是用户线程

线程的同步

多个线程操作同一个资源

同步方法/同步块

通过 synchronized 关键字修饰的方法/代码块

每个对象都有一把锁

方法中需要修改的内容才需要锁

synchronized (obj)
{
    //对某个对象加锁
}

synchronized void function()
{
    // 方法体。。。
    // 对这个对象加锁
}

synchronized(obj),obj称为同步监视器

  1. Obj可以是任何对象
  2. 同步方法中无需指定同步监视器,同步方法的同步监视器就是this

CopyOnWriteArrayList

线程安全的一个List

Lock 锁

通过显示定义同步锁对对象来实现同步。同步锁使用Lock对象充当,Lock是可重如锁

ReentrantLock类实现了Lock,和synchronized的并发性和内存语义

private int num = 10;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
    while (true){
        lock.lock();
        System.out.println(num--);
        if (num<0)
            return;
        lock.unlock();
    }
}

Lock锁的性能更好

线程的通信

  1. wait()
  2. notify()
  3. notifyAll()

wait()

会释放锁

notify()和notifyAll()的区别

参考: https://www.zhihu.com/question/37601861/answer/94679949

  • 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁
  • 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

线程池

提前创建好多个线程,放入线程迟中,使用时直接获取,使用完放回迟中

便于线程管理

ExecutorService

线程池对象

  1. execute(Runnable runnable) 执行
  2. Future future = service.submit(new TestCallable()); 执行
  3. shutdown() 关闭线程池

Executors

调用这个方法创建ExecutorService

//创建容量为 10 的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);