程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程则是执行程序的依次执行过程,它是一个动态的概念。是系统资源分配的单位。
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。
注意:
很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。
自定义线程类继承Thread类
重写**run()**方法,编写线程执行体
创建线程对象,调用**start()**方法启动线程
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在执行run方法-->" + i);
SleepUtil.sleep(100);
}
}
public static void main(String[] args) {
TestThread1 thread = new TestThread1();
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在执行main方法-->" + i);
SleepUtil.sleep(100);
}
}
}
自定义线程类实现Runnable接口
实现**run()**方法,编写线程执行体
创建线程对象,调用**start()**方法启动对象
//创建线程方式二:实现runnable接口
public class TestThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run方法正在执行-->" + i);
SleepUtil.sleep(10);
}
}
public static void main(String[] args) {
TestThread2 thread2 = new TestThread2();
new Thread(thread2).start();
for (int i = 0; i < 20; i++) {
System.out.println("main方法正在执行-->" + i);
SleepUtil.sleep(10);
}
}
}
public class TestThread3 implements Runnable {
private int ticketNumber = 10;
@Override
public void run() {
while (ticketNumber >= 0) {
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNumber-- + "张票");
SleepUtil.sleep(2);
}
}
public static void main(String[] args) {
TestThread3 thread3 = new TestThread3();
new Thread(thread3, "段金宇").start();
new Thread(thread3, "孙宜辰").start();
new Thread(thread3, "黄牛").start();
}
}
问题:多个线程抢夺资源会出现资源重复使用
//案例二:龟兔赛跑
public class TestThread4 implements Runnable {
private String winner;
@Override
public void run() {
int length = 100;
while (length > 0) {
if (Thread.currentThread().getName().equals("兔子")) {
//模拟兔子中间休息
if (length == 50) {
SleepUtil.sleep(300);
}
//兔子每次跑两米
length = length - 2;
} else {
//乌龟每次跑一米
length = length - 1;
}
SleepUtil.sleep(5);
//判断比赛是否结束
if (isOver(length)) {
break;
}
System.out.println(Thread.currentThread().getName() + "距离终点还有-->" + length + "米");
}
}
public boolean isOver(int len) {
if (winner != null) return true;
if (len <= 0) {
winner = Thread.currentThread().getName();
System.out.println("比赛结束,胜利者是" + Thread.currentThread().getName());
return true;
}
return false;
}
public static void main(String[] args) {
TestThread4 thread4 = new TestThread4();
new Thread(thread4, "兔子").start();
new Thread(thread4, "乌龟").start();
}
}
实现Callable接口,需要返回值类型
重写call方法,需要抛出异常
创建目标对象
创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
提交执行:Future result1 = ser.submit(11);
获取结果:boolean r1 = result1.get()
关闭服务:ser.shutdownNow();
public class TestThread5 implements Callable {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"正在执行-->" + i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestThread5 thread1 = new TestThread5();
TestThread5 thread2 = new TestThread5();
//创建执行服务
ExecutorService pool = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> t1 = pool.submit(thread1);
Future<Boolean> t2 = pool.submit(thread1);
//获得结果
Boolean res1 = t1.get();
Boolean res2 = t2.get();
if (res1 == true) System.out.println("线程一完成任务");
if (res2 == true) System.out.println("线程二完成任务");
//关闭服务
pool.shutdown();
}
}
执行结果:
优点:
继承Thread类 | 实现Runnable接口 |
---|---|
子类继承Thread类具备多线程能力 | 实现接口RUnnable具有多线程能力 |
子类对象.start() | 传入目标对象+Thread对象.start() |
不建议使用:避免OOP单继承局限性 | 推荐使用:便面单继承局限性、灵活方便,同一个对象可被多个线程使用 |
//结婚接口
public interface TestProxy1 {
void happyMarry();
}
//结婚主人公:被代理者
class You implements TestProxy1 {
@Override
public void happyMarry() {
System.out.println("段金宇结婚当日......");
}
}
//婚庆公司:代理者
class WeddingCompany implements TestProxy1 {
private TestProxy1 target;
public WeddingCompany(TestProxy1 target) {
this.target = target;
}
@Override
public void happyMarry() {
before();
target.happyMarry();
after();
}
private void before() {
System.out.println("布置婚礼现场");
}
private void after() {
System.out.println("收分子钱");
}
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.happyMarry();
}
}
实现方法:
new Thread(() -> System.out.println("这是lambda表达式" + Thread.currentThread().getName())).start();
public class TestThread2 implements Runnable{
@Override
public void run() {
}
}
}
外部类
public class TestProxy3 {
public static void main(String[] args) {
Like like = new ILike();
like.lambda();
}
}
interface Like{
void lambda();
}
class ILike implements Like{
@Override
public void lambda() {
System.out.println("i like");
}
}
静态内部类
public class TestProxy4 {
public static class ILike implements Like{
@Override
public void lambda() {
System.out.println("i Like");
}
}
public static void main(String[] args) {
ILike iLike = new ILike();
iLike.lambda();
}
}
局部内部类
public class TestProxy5 {
public static void main(String[] args) {
ILike iLike = new ILike();
iLike.lambda();
class ILike implements Like {
@Override
public void lambda() {
System.out.println("i Like");
}
}
}
}
public class TestProxy5 {
public static void main(String[] args) {
Like like = new Like() {
@Override
public void lambda() {
System.out.println("i like");
}
};
like.lambda();
}
}
Lambda表达式
public class TestProxy6 {
public static void main(String[] args) {
Like like1 = () -> System.out.println("i like");
like1.lambda();
}
}
方法 | 说明 |
---|---|
void setPriority(int newPriority) | 更改线程的优先级 |
void sleep(long millis) | 指定线程休眠毫秒数 |
void join() | 等待该线程终止 |
void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程(不建议使用) |
boolean isAlive() | 测试线程是否处于活动状态 |
不推荐使用JDK提供的stop()、destory()方法。【已废弃】
推荐线程自己停下来
public class TestStatus1 implements Runnable {
//定义执行标识
private boolean flag = true;
@Override
public void run() {
//在线程体中使用该标识
while (flag) {
System.out.println("run ... Thread");
}
}
//对外提供方法改变标识
public void stop() {
this.flag = false;
}
}
案例:每秒打印当前系统时间
public class TestSleep implements Runnable {
@Override
public void run() {
while (true) {
Date date = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TestSleep testSleep = new TestSleep();
new Thread(testSleep).start();
}
}
public class TestYield implements Runnable {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + ":start....");
Thread.yield();
System.out.println("线程" + Thread.currentThread().getName() + ":end....");
}
public static void main(String[] args) {
TestYield testYield = new TestYield();
new Thread(testYield, "a").start();
new Thread(testYield, "b").start();
}
}
运行结果
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("vip线程正在执行...." + i);
SleepUtil.sleep(100);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("普通线程在执行...." + i);
SleepUtil.sleep(100);
if (i == 5) {
thread.join();
SleepUtil.sleep(50);
}
}
}
}
从第六次循环开始,vip线程插队执行,直到执行完普通线程继续执行
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED
已退出的线程处于此状态。
public class TestWatchStatus {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 2; i++) {
SleepUtil.sleep(100);
System.out.println("子线程");
}
System.out.println("/");
});
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());
SleepUtil.sleep(100);
while (thread.getState().equals(Thread.State.TIMED_WAITING)) {
SleepUtil.sleep(100);
System.out.println(thread.getState());
}
SleepUtil.sleep(1000);
System.out.println(thread.getState());
}
}
观察结果:
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
线程的优先级用数字表示,范围从1~10。
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
改变优先级的方法
thread.setPriority();
public class TestPriority implements Runnable {
public static void main(String[] args) {
TestPriority testPriority = new TestPriority();
Thread thread = new Thread(testPriority);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
thread = new Thread(testPriority);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
thread = new Thread(testPriority);
thread.setPriority(4);
thread.start();
thread = new Thread(testPriority);
thread.setPriority(7);
thread.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--正在执行" + "其优先级为:" + Thread.currentThread().getPriority());
}
}
优先级数值越大越先执行
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
eg:后台记录操作日志,监控内存,垃圾回收等待…
public class TestDaemon {
public static void main(String[] args) {
new Thread(new You()).start();
Thread thread = new Thread(new China());
//设置为守护线程,默认为false
thread.setDaemon(true);
thread.start();
}
}
class You implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("你在快乐的活着");
SleepUtil.sleep(50);
}
}
}
class China implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("国家在默默守护者你");
SleepUtil.sleep(50);
}
}
}
虚拟机不会等待守护线程结束
并发:同一个对象被多个线程同时操作
上万人同时抢票
两个银行同时取钱
线程同步的形成条件:队列+锁
public class demo1_buyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "段金宇").start();
new Thread(buyTicket, "孙宜辰").start();
new Thread(buyTicket, "黄牛党").start();
}
}
class BuyTicket implements Runnable {
private Integer ticketNumber = 10;
@Override
public void run() {
while (true) {
boolean b = buy();
if (!b) {
break;
}
SleepUtil.sleep(1000);
}
}
private synchronized boolean buy() {
if (ticketNumber > 0) {
System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNumber + "张票");
ticketNumber--;
return true;
}
return false;
}
}
未同步上锁时的结果
public class demo2_withdraw {
public static void main(String[] args) {
Bank bank = new Bank();
new Thread(bank, "段金宇").start();
new Thread(bank, "孙宜辰").start();
}
}
class Bank implements Runnable {
private int money = 100;
@Override
public void run() {
withdraw(60);
}
private void withdraw(int rest) {
if (money > rest) {
System.out.println(Thread.currentThread().getName() + "取了60万元");
money = money - rest;
System.out.println("账户剩余:" + money + "万元");
} else {
System.out.println("余额不足,去签失败");
}
}
}
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
存在以下问题︰
同步方法:
缺陷:若将一个大方法声明为synchronized将会影响效率
private synchronized void withdraw(int rest) {
if (money > rest) {
System.out.println(Thread.currentThread().getName() + "取了60万元");
money = money - rest;
System.out.println("账户剩余:" + money + "万元");
} else {
System.out.println("余额不足,取钱失败");
}
}
private synchronized boolean buy() {
if (ticketNumber > 0) {
System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNumber + "张票");
ticketNumber--;
return true;
}
return false;
}
同步块:synchronized (Obj) {}
Obj称之为同步监视器
同步监视器的执行过程
1.第一个线程访问,锁定同步监视器,执行其中代码;
2.第二个线程访问,发现同步监视器被锁定,无法访问;
3.第一个线程访问完毕,解锁同步监视器;
4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问。
买票案例优化
private boolean buy() {
synchronized (ticketNumber) {
if (ticketNumber > 0) {
System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNumber + "张票");
ticketNumber--;
return true;
}
}
return false;
}
private void withdraw(int rest) {
synchronized (money){
if (money > rest) {
System.out.println(Thread.currentThread().getName() + "取了60万元");
money = money - rest;
System.out.println("账户剩余:" + money + "万元");
} else {
System.out.println("余额不足,取钱失败");
}
}
}
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
SleepUtil.sleep(3000);
System.out.println(list.size());
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
SleepUtil.sleep(3000);
System.out.println(list.size());
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
SleepUtil.sleep(100);
System.out.println(list.size());
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有**“两个以上对象的锁”**时,就可能会发生“死锁”的问题。
public class TestDeadEmbrace {
public static void main(String[] args) {
Makeup lady1 = new Makeup(0, "孙宜辰");
Makeup lady2 = new Makeup(1, "孙二辰");
lady1.start();
lady2.start();
}
}
class Lipstick {
}
class Mirror {
}
class Makeup extends Thread {
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choose;
String name;
public Makeup(int choose, String name) {
this.choose = choose;
this.name = name;
}
@Override
public void run() {
makeup();
}
private void makeup() {
if (choose == 0) {
synchronized (lipstick) {
System.out.println(this.name + "拿到了" + Lipstick.class.getName());
SleepUtil.sleep(100);
synchronized (mirror) {
System.out.println(this.name + "拿到了" + Mirror.class.getName());
}
}
} else {
synchronized (mirror) {
System.out.println(this.name + "拿到了" + Mirror.class.getName());
SleepUtil.sleep(200);
synchronized (lipstick) {
System.out.println(this.name + "拿到了" + Lipstick.class.getName());
}
}
}
}
}
系统资源的竞争
通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。
进程推进顺序非法
进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞。
产生死锁必须同时满足以上四个条件,只要其中任一条件不成立,死锁就不会发生。
public class TestLock {
public static void main(String[] args) {
Station station = new Station(10);
new Thread(station, "段金宇").start();
new Thread(station, "孙宜辰").start();
new Thread(station, "黄牛" +
"").start();
}
}
class Station implements Runnable {
private int ticketNumber;
private boolean flag = true;
public Station(int ticketNumber) {
this.ticketNumber = ticketNumber;
}
@Override
public void run() {
while (flag) {
flag = buyTicket();
}
}
private boolean buyTicket() {
Lock lock = new ReentrantLock();
try {
lock.lock();
if (ticketNumber > 0) {
System.out.println(Thread.currentThread().getName() + "取走了第" + ticketNumber-- + "张票");
SleepUtil.sleep(100);
return true;
}
} finally {
lock.unlock();
}
return false;
}
}
lock()方法要写在try中。
9 总结
synchroinzed与lock对比
synchronized | lock |
---|---|
显示锁(手动开启和关闭锁) | 隐式锁(出了作用域自动释放) |
锁代码块 | 既可以锁代码块,也可以锁方法 |
非公平锁(无法保证线程按照锁的顺序获得锁,效率较高) | 可配置 |
不可中断 | 可中断 |
读写,读读,写写均互斥 | ReadWriteLock使读读可以并发执行 |
都是可重入锁 |
可重入锁:持有该锁的资源时可再次对改资源上锁,避免死锁
原理:通过为每个锁关联一个请求计数器和一个获得该锁的线程。当计数器为0时,认为锁是未被占用的。线程请求一个未被占用的锁时,JVM将记录该线程并将请求计数器设置为1,此时该线程就获得了锁,当该线程再次请求这个锁,计数器将递增,当线程退出同步方法或者同步代码块时,计数器将递减,当计数器为0时,线程就释放了该对象,其他线程才能获取该锁。
应用场景:生产者消费者模式
方法名 | 作用 |
---|---|
wait() | 线程会一直等待,直到其他线程通知。与sleep不同,线程会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个语出等待状态的线程 |
notiftAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度 |
实质是线程同步问题,生产者与消费者共享同一资源,并且之间相互依赖,互为条件。
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
public class TestMonitor {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Consumer consumer = new Consumer(container);
Producer producer = new Producer(container);
new Thread(producer, "1号").start();
new Thread(producer, "2号").start();
new Thread(consumer, "段金宇").start();
new Thread(consumer, "孙宜辰").start();
}
}
class Produce {
private String id;
public Produce(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
class SynContainer {
//容器的大小
Produce[] produces = new Produce[10];
//产品数量
int count = 0;
public synchronized void push(Produce produce) throws InterruptedException {
while (count >= produces.length - 1) this.wait();
produces[count] = produce;
count++;
//通知所有消费者消费
notifyAll();
}
public synchronized Produce pop() throws InterruptedException {
while (count <= 0) this.wait();
count--;
Produce produce = produces[count];
notifyAll();
return produce;
}
}
class Producer implements Runnable {
private SynContainer container;
public Producer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
produce();
}
public void produce() {
while (true) {
String id = UUID.randomUUID().toString().replace("-", "").substring(0, 5);
Produce produce = new Produce(id);
try {
container.push(produce);
System.out.println("生产者" + Thread.currentThread().getName() + "生产了" + id + "号产品");
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
private SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
while (true) {
try {
Produce produce = container.pop();
System.out.println("消费者" + Thread.currentThread().getName() + "消费了" + produce.getId() + "号产品");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
设置标志位flag表明当前消费者或生产者谁可以行动。
public class TestThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在运行");
}
}