Thread常用方法
方法 | 说明 |
---|---|
static int activeCount() | 返回当前线程的线程组及其子组中活动线程数量的估计值。 |
void start() | 使该线程开始执行;Java虚拟机调用这个线程的run方法。 |
static Thread currentThread() | 获取当前执行线程引用 |
long getId() | 获取线程id |
String getName() | 获取线程名 |
void setName(String name) | 更改线程的名称。 |
int getPriority() | 获取线程优先级值 |
int setPriority(int priority) | 更改线程的优先级。 |
Thread.State getState() | 获取线程状态 |
void interrupt() | 中断这个线程 |
boolean isInterrupted() | 查看线程是否被中断。 |
static boolean interrupted() | 测试当前线程是否已被中断,调用这个方法会将中断标识恢复成false。 |
static void yield() | 向调度程序提示,当前线程愿意放弃当前线程对处理器的使用。 |
void run() | 线程运行时执行的会调用此方法 |
void sleep(long miliseconds) | 使当前正在执行的线程休眠(暂时停止执行)达指定的毫秒数。 |
void join() | 等待线程死亡。 |
void join(long miliseconds) | 按指定的毫秒数等待线程死亡。 |
boolean isAlive() | 测试线程是否处于活动状态。 |
void suspend() | 用于挂起线程(已弃用)。 |
void resume() | 用于恢复挂起的线程(已弃用)。 |
void stop() | 用于停止线程(已弃用)。 |
boolean isDaemon() | 测试该线程是否为守护线程。 |
void setDaemon(boolean b) | 将线程标记为守护线程,需在调用start方法前设置。 |
启动线程的四种方式
继承Thread
class UseThread extends Thread {
@Override
public void run() {
System.out.println("UseThread object run ...");
}
public static void main(String[] args) {
UseThread useThread = new UseThread();
useThread.start();
}
}
实现Runnable
class UseRunnable implements Runnable {
@Override
public void run() {
System.out.println("UseRunnable object run ...");
}
public static void main(String[] args) {
UseRunnable useRunnable = new UseRunnable();
Thread thread = new Thread(useRunnable);
thread.start();
}
}
实现Callable
class UseCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("UseCallable object call ...");
return "call result";
}
public static void main(String[] args) {
UseCallable callable = new UseCallable();
FutureTask task = new FutureTask(callable);
Thread thread = new Thread(task);
thread.start();
try {
System.out.println("线程运行返回:" + task.get());
} catch (InterruptedException | ExecutionException e) {
task.cancel(true);//取消计算
e.printStackTrace();
}
}
}
使用Executors
class NewThread {
public static void main(String[] args) {
Thread t = Executors.defaultThreadFactory().newThread(() -> System.out.println("使用Executors创建线程..."));
t.start();
}
}
使用实现Runnable的好处
- 避免了单继承的局限性,一个类可以实现多个接口
- 适合于资源的共享
以卖票为例,使用继承Thread:
class Ticket {
private int ticketNum;
public Ticket(int ticketNum) {
this.ticketNum = ticketNum;
}
public void sell() {
while (true) {
synchronized (this) {
if (ticketNum <= 0) break;
ticketNum--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩[" + ticketNum + "]张票");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TicketThread extends Thread {
private Ticket ticket;
public TicketThread(Ticket ticket, String threadName) {
super(threadName);
this.ticket = ticket;
}
@Override
public void run() {
ticket.sell();
}
public static void main(String[] args) {
//3个线程卖10张票
Ticket ticket = new Ticket(10);
new TicketThread(ticket, "线程1").start();
new TicketThread(ticket, "线程2").start();
new TicketThread(ticket, "线程3").start();
}
}
使用实现Runnable实现:
class TicketRunnable implements Runnable {
private int ticketNum;
public TicketRunnable(int ticketNum) {
this.ticketNum = ticketNum;
}
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticketNum <= 0) break;
ticketNum--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩[" + ticketNum + "]张票");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//3个线程卖10张票
TicketRunnable ticket = new TicketRunnable(10);
new Thread(ticket, "线程1").start();
new Thread(ticket, "线程2").start();
new Thread(ticket, "线程3").start();
}
}
安全的停止线程方法
public class RunnableImpl implements Runnable {
@Override
public void run() {
//当执行中断方法interrupt()后,线程并不会停止,而是会继续执行完,线程与线程之间是一种协作状态
// while(true){
// System.out.println("UseThread object run ...");
// }
//需要受到控制的话,使用isInterrupted()方法判断一下
// while (!Thread.currentThread().isInterrupted()) {
// System.out.println(Thread.currentThread().getName() + " is run ...");
// }
//使用interrupted()方法判断中断状态会将中断标识恢复成false
// while (!Thread.currentThread().interrupted()) {
// System.out.println(Thread.currentThread().getName() + " is run ...");
// }
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + " is run ...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
//抛出InterruptedException异常后中断标识会恢复成false
//如果抛异常后需要中断,需要在调用interrupt()方法进行中断操作
Thread.currentThread().interrupt();
}
}
System.out.println(Thread.currentThread().getName() + " interrupt flag is:" + Thread.currentThread().isInterrupted());
}
public static void main(String[] args) throws InterruptedException {
RunnableImpl runnable = new RunnableImpl();
Thread t = new Thread(runnable);
t.start();
Thread.sleep(2000);
t.interrupt();
}
}
线程的停止由线程本身决定,使用线程的引用调用interrupt()方法只是向线程打个招呼。
线程的stop、resume、suspend方法不建议使用,stop方法可以导致线程不正确的释放资源,suspend方法容易导致线程死锁
守护线程
守护线程是同主线程一起死亡的线程,try中的finally块中的内容不一定会执行。
设置守护线程方法:在线程调用start()方法之前调用线程的void setDaemon(boolean on)方法,设置为ture,再调用start()方法,这个线程即为守护线程。
示例
/**
* @CalssName DaemonThreadDemo
* @Description 守护进程
* @since JDK 1.8
*/
public class DaemonThreadDemo {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("I use Lambda expression...");
}
} finally {
System.out.println("finally code...");
}
});
t.setDaemon(true);
t.start();
Thread.sleep(50);
}
}
线程优先级
设置线程优先级示例
/**
* @CalssName ThreadPriority
* @Description 线程优先级
* @since JDK 1.8
*/
public class ThreadPriority {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
int t1count = 0, t2count = 0;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
synchronized (this) {
String tName = Thread.currentThread().getName();
if ("线程1".equals(tName)) {
t1count++;
System.out.println(tName + " run... " + t1count + "次");
}
if ("线程2".equals(tName)) {
t2count++;
System.out.println(tName + " run... " + t2count + "次");
}
}
}
}
};
Thread t1 = new Thread(runnable, "线程1");
Thread t2 = new Thread(runnable, "线程2");
t1.setPriority(Thread.MAX_PRIORITY);//最大优先级10
t2.setPriority(Thread.MIN_PRIORITY);//最小优先级1
t1.start();
t2.start();
}
}
运行效果如下
线程优先级根据系统不同,运行时的实现方式不同,并不是设置的越高,CPU就一定会优先执行
synchronized内置锁
对象锁:对象锁锁的是类new出来的对象
类锁:类锁锁的是类的Class对象,Java虚拟机会保证每个类只有一个Class对象
类锁和对象锁运行示例
public class SynchronizedDemo {
//对象锁
public synchronized void test1() {
synchronized (this) {
//这也是对象锁
System.out.println("方法中使用对象锁...");
}
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(600);
} catch (InterruptedException ie) {
}
}
}
//类锁
public synchronized static void test2() {
synchronized (SynchronizedDemo.class) {
//这个也是类锁
System.out.println("方法中使用类锁...");
}
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(250);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final SynchronizedDemo myt2 = new SynchronizedDemo();
Thread test1 = new Thread(new Runnable() {
public void run() {
myt2.test1();
}
}, "test1");
Thread test2 = new Thread(new Runnable() {
public void run() {
SynchronizedDemo.test2();
}
}, "test2");
test1.start();
test2.start();
}
}
运行结果
方法中使用对象锁...
test1 : 4
方法中使用类锁...
test2 : 4
test2 : 3
test2 : 2
test1 : 3
test2 : 1
test2 : 0
test1 : 2
test1 : 1
test1 : 0
volatile的使用
volatile不是线程安全的,它能保证数据的可见性,不能保证数据的原子性。
volatile适用于只有一个线程写的,多个线程读的情况下使用,它的并发效率较高。
使用示例:
public class VolatileDemo {
private volatile int score;
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
//this.score = score + 10;//这种做法错误,因为volatile不能保证score的原子性
}
}
ThreadLocal的使用
多个线程使用ThreadLocal变量的时候,各个线程均使用这个变量的副本,不会访问到其它线程的这个ThreadLocal变量值。
方法 | 方法说明 |
---|---|
public T get() | 返回当前线程中ThreadLocal变量副本的值 |
protected T initialValue() | 返回当前线程中ThreadLocal变量副本的初始值 |
public void remove() | 删除当前线程中ThreadLocal变量副本的值 |
public void set(T value) | 设置当前线程中ThreadLocal变量副本的值 |
下面是ThreadLocal变量和普通变量的区别示例
/**
* @CalssName UseThreadLocal
* @Description ThreadLocalDemo
* @since JDK 1.8
*/
public class UseThreadLocal implements Runnable {
private int count = 0;
private ThreadLocal<Integer> threadLocal = new ThreadLocal() {
@Override
protected Integer initialValue() {
return 0;
}
};
@Override
public void run() {
String threadName = Thread.currentThread().getName();
synchronized (this) {
for (int i = 1; i <= 3; i++) {
this.count += this.count + i;
threadLocal.set(threadLocal.get() + i);
System.out.println(threadName + " this.count:" + this.count);
System.out.println(threadName + " threadLocal value:" + threadLocal.get());
}
}
}
public static void main(String[] args) {
UseThreadLocal useThreadLocalRunnable = new UseThreadLocal();
new Thread(useThreadLocalRunnable, "线程1").start();
new Thread(useThreadLocalRunnable, "线程2").start();
new Thread(useThreadLocalRunnable, "线程3").start();
}
}
线程的等待与通知
wait():当前线程处于等待状态,并释放锁,既然要释放锁就必须先获得锁,而线程进入synchronized修饰的方法或者代码块中才能获得锁,所以wait()方法只能在synchronized修饰的方法或者代码块中才能使用。
notify():随机通知并唤醒一个使用wait()方法等待的线程。
notifyAll():通知并唤醒所有使用wait()方法等待的线程。
尽量使用notifyAll()方法,因为使用notify()有可能发生信号丢失的情况。
等待与通知的标准范式
- 等待线程获取到对象的锁,调用wait()方法,放弃锁,进入等待队列
- 通知线程获取到对象的锁,调用对象的notify()方法
- 等待线程接受到通知,从等待队列移到同步队列,进入阻塞状态
- 通知线程释放锁后,等待线程获取到锁继续执行
下面是使用wait()、notifyAll和synchronized内置锁做的简易链接池工具
/**
* @CalssName DBPool
* @Description 数据库链接池工具
* @since JDK 1.8
*/
public class DBPool {
private static LinkedList<Connection> pool = new LinkedList<>();
private static String url = "jdbc:mysql://192.168.86.101:3306/mysql";
private static String username = "root";
private static String password = "my-pwd";
/**
* @return java.sql.Connection
* @Title getConnection
* @Description 从链接池中获取链接,如果没有链接了在指定有效时间内获取链接,不然返回null
* @Param [mills] 没有链接时指定获取去时间
* @Throws InterruptedException
*/
public Connection getConnection(long mills) throws InterruptedException {
synchronized (pool) {
if (mills <= 0) {
while (pool.isEmpty()) {
//链接被用光了,等待回收链接
pool.wait();
}
//有链接了
return pool.removeFirst();
} else {
long overTime = System.currentTimeMillis() + mills;
long remain = mills;
while (pool.isEmpty() && remain > 0) {
//没链接了,等待指定时间
pool.wait(remain);
remain = overTime - System.currentTimeMillis();
}
// 指定时间后还没有链接就返回null
if (pool.isEmpty()) return null;
return pool.removeFirst();
}
}
}
/**
* @CalssName DBPool
* @Description 释放链接
* @since JDK 1.8
*/
public void releaseConnection(Connection connection) {
if (connection != null) {
synchronized (pool) {
pool.addLast(connection);
//链接回收了,通知其他等待的线程使用
pool.notifyAll();
}
}
}
/**
* @CalssName DBPool
* @Description 初始化链接池
* @since JDK 1.8
*/
public DBPool(int initSize) {
if (initSize > 0) {
try {
Class.forName("com.mysql.jdbc.Driver");
for (int i = 0; i < initSize; i++) {
Connection connection = DriverManager.getConnection(url, username, password);
pool.add(connection);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new DBPool(10);
System.out.println(pool.size());
}
}
Join方法
如果线程A调用线程B的join()方法之后就必须等待线程B执行完,线程A才会执行
示例
/**
* @CalssName ThreadUseJoin
* @Description 使用join
* @since JDK 1.8
*/
public class ThreadUseJoin {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ",i:" + i);
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
try {
t1.join();//等t1线程执行完销毁才开始执行
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ",i:" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2");
t2.start();
}
}
Yield方法
yield方法执行后并不会释放锁,而是放弃当前在CPU的运行权力,等待下一次CPU调度执行。
执行sleep和notify、notifyAll方法也不会释放锁,notify、notifyAll执行之后唤醒其他线性,但唤醒的以及其他线程仍然处于阻塞状态,直到当前synchronized修饰的代码执行完其他线程才会获取锁