Java 多线程
三种创建方式
- 继承 Thread 类
- 实现 Runnable 接口
- 实现 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();
}
}
线程方法
- setPriority(int newPriority) 更改线程的优先级
- sleep(long millis) 让该进程休眠
- join() 等待该线程终止
- yield() 暂停当前执行的线程对象
- interrupt() 中断线程
- 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
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
thread.setDaemon(true); // 代表这个线程是一个守护线程
//如果是 false 是用户线程
线程的同步
多个线程操作同一个资源
同步方法/同步块
通过 synchronized 关键字修饰的方法/代码块
每个对象都有一把锁
方法中需要修改的内容才需要锁
synchronized (obj)
{
//对某个对象加锁
}
synchronized void function()
{
// 方法体。。。
// 对这个对象加锁
}
synchronized(obj)
,obj称为同步监视器
- Obj可以是任何对象
- 同步方法中无需指定同步监视器,同步方法的同步监视器就是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锁的性能更好
线程的通信
- wait()
- notify()
- notifyAll()
wait()
会释放锁
notify()和notifyAll()的区别
- 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
- 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
- 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
线程池
提前创建好多个线程,放入线程迟中,使用时直接获取,使用完放回迟中
便于线程管理
ExecutorService
线程池对象
- execute(Runnable runnable) 执行
- Future
future = service.submit(new TestCallable()); 执行 - shutdown() 关闭线程池
Executors
调用这个方法创建ExecutorService
//创建容量为 10 的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);