Java多线程编程入门指南
进程是资源分配的单位,线程是调度的单位。一个进程中可以开启多个线程运行,多个线程共享进程的资源,另外也有自己的栈空间。
Java实现多线程
在java中实现多线程有以下几种方式:
- 继承
Thread
类,本质是重写Runnable
接口 new Thread
传入一个Runnable
对象- 匿名类对象实现
public class HelloWorld {
public static void main(String[] args) {
out.println("This is main thread: " + Thread.currentThread().getName());
TestThread thread = new TestThread();
thread.start();
}
}
class TestThread extends Thread {
@Override
public void run() {
out.println("Current Thread: " + Thread.currentThread().getName());
}
}
public class HelloWorld {
public static void main(String[] args) {
out.println("This is main thread: " + Thread.currentThread().getName());
Thread thread = new Thread(new TestRunnable());
thread.start();
}
}
class TestRunnable implements Runnable {
@Override
public void run() {
out.println("Current is runnable: " + Thread.currentThread().getName());
}
}
public class HelloWorld {
public static void main(String[] args) {
out.println("This is main thread: " + Thread.currentThread().getName());
new Thread(new Runnable() {
@Override
public void run() {
out.println("current thread is " + Thread.currentThread().getName());
}
}).start();
}
}
线程的状态
- 创建状态: 当创建
Thread
时就处于创建状态已经分配好资源,等待运行。 - 就绪状态:调用
start
启动该线程,启动后进入线程队列排队,此时CPU调度 - 运行状态:就绪状态的线程获得处理器的资源时,线程就进入了运行状态,此时将自动调用
run
方法。 - 阻塞状态:正在运行的线程在某些特殊情况下,如:当前线程调用
sleep
、wait
等方法时,运行在当前线程里的其它线程调用join
方法时,以及等待用户输入的时候。只有当引起阻塞原因消失后,线程才能进入就绪状态。资源的竞争等待。 - 终止状态:当线程
run
方法运行结束后,或者主线程的main()
方法结束后,线程才能处于终止状态,线程一旦死亡就不能复生。
关键函数
- 线程命名:
setName(String)
- 线程休眠:
Thread.sleep(timeInMilli)
当前线程从“运行状态”进入到“阻塞状态”。sleep方法会指定休眠时间,线程休眠的时间会大于或等于该休眠时间,该线程会被唤醒,此时它会由“阻塞状态”变成“就绪状态”,然后等待CPU的调度执行。 - 线程中断:
Thread.interrupt()
该方法将会设置该线程的中断状态位,即设置为true,中断的结果线程是终止状态、还是阻塞状态或是继续运行至下一步,就取决于该程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(即中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。 - 线程强制执行:
Thread.join()
设置该线程强制执行完后其他线程才能执行。 - 线程让步:
Thread.yield()
yeild是将线程由“运行状态”转别为“就绪状态”, 其他线程是否执行还是要看CPU调度
线程同步
多线程访问共享资源必须要考虑线程同步,否则安全性几乎为0。同步就是指多个线程在同一个时间段内只能有一个线程执行指定的代码,其他线程要等待此线程完成之后才可以继续进行执行,在Java中提供有synchronized
关键字以实现同步处理,同步的关键是要为代码加上“锁”。三种方法:
- 同步代码块
- 同步方法
Lock
实现
数据错乱
public class HelloWorld {
public static void main(String[] args) {
SaleTicket ticket = new SaleTicket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
Thread t4 = new Thread(ticket);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class SaleTicket implements Runnable {
private int ticket = 10;
@Override
public void run() {
while (true) {
if (ticket < 0) {
out.println(Thread.currentThread().getName() + " sale No." + ticket);
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
out.println(Thread.currentThread().getName() + " remain : " + ticket--);
}
}
}
Thread-3 remain : 10
Thread-2 remain : 9
Thread-0 remain : 9
Thread-1 remain : 9
Thread-3 remain : 7
Thread-2 remain : 8
Thread-0 remain : 6
Thread-1 remain : 5
Thread-2 remain : 4
Thread-3 remain : 3
Thread-1 remain : 2
Thread-0 remain : 1
Thread-2 remain : 0
Thread-2 sale No.-1
Thread-3 remain : -1
Thread-3 sale No.-2
Thread-1 remain : -2
Thread-1 sale No.-3
Thread-0 remain : -3
Thread-0 sale No.-4
按照单线程的理解肯定不会出现负数,但是在多线程情况下,可能一个线程已经将数据改为0了,但是另外一个线程还是会执行–操作,导致负数。
同步方法
- 同步代码块
synchronized(需要同步的对象){
需要同步的操作
}
class SaleTicket implements Runnable {
private int ticket = 10;
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticket < 0) {
out.println("==============over===============");
break;
}
out.println(Thread.currentThread().getName() + " remain : " + ticket--);
}
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Thread-0 remain : 10
Thread-3 remain : 9
Thread-2 remain : 8
Thread-1 remain : 7
Thread-0 remain : 6
Thread-3 remain : 5
Thread-2 remain : 4
Thread-1 remain : 3
Thread-0 remain : 2
Thread-3 remain : 1
Thread-2 remain : 0
Thread-1==============over===============
Thread-0==============over===============
Thread-3==============over===============
Thread-2==============over===============
当多个线程执行run方法时,只要一个线程在执行包裹的代码块,那么其他线程就会一直等待,直到占有该临界区的线程执行完毕才会执行。因此不会出现剩余票数为负数。
同步方法
修饰符 synchronized 返回类型 函数名()
class SaleTicket implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (sale()) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
private synchronized boolean sale() {
if (ticket < 0) {
out.println(Thread.currentThread().getName() + "==============over===============");
return false;
}
out.println(Thread.currentThread().getName() + " remain : " + ticket--);
return true;
}
}
Lock
Lock.lock
获得锁, Lock.unclock()
放开锁