java之线程

22 阅读2分钟

线程生命周期

image.png

线程方法

静态方法

  1. currentThread 获取当前线程

实例方法

  1. getName() 获取线程名字
  2. setName() 设置线程名字
  3. yield() 放弃CPU执行权,进入就绪状态

线程创建方式

有四种方式创建线程

  1. 继承Thread的子类,每个线程的任务都不相同
  2. Runnable接口的实现类,多个线程可以共享同一任务对象,任务数据可共享
  3. Callable接口的实现类,多个线程可以共享同一任务对象,任务数据可共享,且可获取线程返回值
  4. 通过线程池创建线程,多个线程可以共享同一任务对象,任务数据可共享,且可获取线程返回值,自动创建线程

继承Thread的子类

package com.thread;

public class ThreadDemo {
    public static void main(String[] args) {
        SubThread thread = new SubThread();
        thread.start();
    }
}

class SubThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}

Runnable接口的实现类

package com.thread;

public class RunnableDemo {
    public static void main(String[] args) {
        CountTask1 thread = new CountTask1();
        Thread containerThread = new Thread(thread, "计数线程");
        containerThread.start();
    }
}

class CountTask1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

Callable实现类

package com.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CountTask countTask = new CountTask();
        FutureTask<Boolean> futureTask = new FutureTask<Boolean>(countTask);
        Thread thread = new Thread(futureTask, "计数线程");
        thread.start();
        while (futureTask.get()) {
            System.out.println("计数线程执行完毕");
            break;
        }
    }
}

class CountTask implements Callable {
    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
        return true;
    }
}

线程池

package com.thread;

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CountTask2 countTask =  new CountTask2();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        Future futureTask = executorService.submit(countTask);
        while ((Boolean)futureTask.get()) {
            System.out.println("计数线程执行完毕");
            break;
        }
    }
}

class CountTask2 implements Callable {
    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
        return true;
    }
}

线程安全

多个线程访问并操作共享数据时会存在线程安全问题 解决方法:

  1. 同步锁synchronized
  2. 可重入锁ReentrantLock

synchronized

同步代码块

  1. 对代码块加同步锁
  2. 多个线程只有共享一个同步锁,才不会发生抢占
  3. 同步锁可以是任意一个对象
package com.threadsafe;

public class SynchronizedBlockDemo {
    public static void main(String[] args) {
        BuyTicketsTask task = new BuyTicketsTask();
        Thread thread1 = new Thread(task, "甲");
        Thread thread2 = new Thread(task, "乙");
        thread1.start();
        thread2.start();
    }
}

class BuyTicketsTask implements Runnable {
    private int tickets = 100;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName()+ "购得第" + tickets + "张票");
                    tickets--;
                    System.out.println("当前余票:" + tickets);
                    Thread.currentThread().yield();
                } else {
                    break;
                }
            }
        }

    }
}

同步方法

  1. 对方法加同步锁
  2. 如果是非静态方法,同步锁为当前对象this
  3. 如果是静态方法,同步锁为当前类BuyTicketsTask1.class
package com.threadsafe;

public class SynchronizedMethodDemo {
    public static void main(String[] args) {
        BuyTicketsTask1 task = new BuyTicketsTask1();
        Thread thread1 = new Thread(task, "甲");
        Thread thread2 = new Thread(task, "乙");
        thread1.start();
        thread2.start();
    }
}

class BuyTicketsTask1 implements Runnable {
    private int tickets = 100;

    public synchronized void buyTickets () {
        if (tickets > 0) {
            System.out.println(Thread.currentThread().getName()+ "购得第" + tickets + "张票");
            tickets--;
            System.out.println("当前余票:" + tickets);
            Thread.currentThread().yield();
        }
    }

    @Override
    public void run() {
        while (tickets > 0) {
            buyTickets();
        }
    }
}

可重入锁ReentrantLock

  1. 可以随时锁,随时解锁
package com.threadsafe;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    public static void main(String[] args) {
        BuyTicketsTask2 task = new BuyTicketsTask2();
        Thread thread1 = new Thread(task, "甲");
        Thread thread2 = new Thread(task, "乙");
        thread1.start();
        thread2.start();
    }
}

class BuyTicketsTask2 implements Runnable {
    private int tickets = 100;
    ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName()+ "购得第" + tickets + "张票");
                    tickets--;
                    System.out.println("当前余票:" + tickets);
                    Thread.currentThread().yield();
                } else {
                    break;
                }
            } finally {
                lock.unlock();
            }
        }

    }
}