多线程
1、线程的使用
1、创建:
继承线程接口:extends Thread
实现运行接口:implements Runnable
使用Callable和futrue创建线程:
package com.zhou.controller;
public class Window extends Thread {
private int ticket = 100;
public static void main(String[] args) {
Window window = new Window();
// window 线程运行的目标对象
Thread t1 = new Thread(window);
Thread t2 = new Thread(window);
Thread t3 = new Thread(window);
// 三个窗口在抢票
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
if (ticket > 0) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
}
}
结果:
...
...
...
...
窗口1:卖票,票号为:7
窗口3:卖票,票号为:6
窗口2:卖票,票号为:5
窗口1:卖票,票号为:4
窗口1:卖票,票号为:4
窗口2:卖票,票号为:2
窗口1:卖票,票号为:1
窗口3:卖票,票号为:0
窗口2:卖票,票号为:-1
2、同步机制
1、方式一:同步代码块(synchronized)
synchronized(同步监视器) {
}
- 说明:操作共享数据的代码即为需要被同步的代码。
- 共享数据:多个线程共同操作的变量。比如:ticket
- 同步监控器:俗称“锁”,任何一个类的对象都可以充当锁。
这里使用 new Object()对象充当锁,
synchronized(object){ ..//锁住这里的代码块..}
为了确保三个windows 对象使用的是同一个锁,必须进行obj对象的唯一和改为static静态的。
package com.zhou.controller;
public class Window extends Thread {
private int ticket = 100;
private static Object obj = new Object();
public static void main(String[] args) {
Window window = new Window();
// window 线程运行的目标对象
Thread t1 = new Thread(window);
Thread t2 = new Thread(window);
Thread t3 = new Thread(window);
// 三个窗口在抢票
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
synchronized(obj) {
if (ticket > 0) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}else {
break;
}
}
}
}
}
问题:为什么不把while(true)锁进去,锁住的话只有一个窗口在执行。
2、方式二:锁代码块(synchronized)
package com.zhou.controller;
public class Window extends Thread {
private static int ticket = 100;
public static void main(String[] args) {
Window window = new Window();
Thread t1 = new Thread(window);
Thread t2 = new Thread(window);
Thread t3 = new Thread(window);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
show();
}
}
private static synchronized void show() {
if (ticket > 0) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
}
总结:
1.同步方法仍然涉及同步监视器,只是我们不需要显示的声明;
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身。
3、方式三:lock锁
创建锁 :private Reentranlock lock = new ReentranLock();
手动开启和关闭锁:try{ lock.lock(); // 开锁 } finally
package com.zhou.controller;
import java.util.concurrent.locks.ReentrantLock;
public class Window extends Thread {
private static int ticket = 100;
private ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Window window = new Window();
Thread t1 = new Thread(window);
Thread t2 = new Thread(window);
Thread t3 = new Thread(window);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
try {
lock.lock(); // 手动调用lock锁
if (ticket > 0) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
} finally {
lock.unlock(); // 手动解锁
}
}
}
}
3、synchronized和lock方式的异同
相同:二者都可以解决线程安全问题
不同:
synchronized 执行相应代码后,自动四方同步监听器。
lock 需要手动启动同步lock(),同步结束后也需要手动实现unlock();
4、线程的5中状态:
1、初始(new)
刚创建的线程
2、就绪(Runnable):调用了start()
线程被其他线程调用了该线程对象的start()方法,进入就绪状态,等待cpu随机的时间调用线程的run()方法。
3、运行(Running):线程获得cpu时间片(timeslice)
正在执行程序代码
4、阻塞(Blocked):因某种原因放弃cpu使用权(wait()、sleep())
放弃cpu使用权:让出了cpu timeslice,暂时停止运行,直到线程进入就绪(Runnable)状态,才有机会再次获得cpu timeslice,然后转到运行(Running)状态。
1、等待阻塞:线程执行了.wait()方法。
jvm把该线程放入到等待队列(waitting queue)中
2、同步阻塞:获取对象同步锁时,该锁被其他线程占用。
jvm把该线程放入锁池(lock pool)中