提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言:
什么是多线程?
多线程是指一个应用程序同时执行多个任务,一般来说一个任务就是一个线程 ,而一个应用程序有一个以上的线程我们称之为多线程。
一、进程和线程的区别
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。
二、在Java中实现线程?
在语言层面有两种方式。java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口,所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。
用Runnable还是Thread?
1.Thread:
新建一个Thread类:
package com.google.demo;
public class BBThread extends Thread{
@Override
public void run() {
while(true) {
System.out.println("hey you get it");
}
}
}
测试使用多线程:
package com.google.demo;
public class Demo {
public static void main (String[] args){
BBThread bbThread = new BBThread();
bbThread.start();
while(true) {
System.out.println("1111111111111111");
}
}
}
结果:
1111111111111111
1111111111111111
hey you get it
1111111111111111
hey you get it
......(省略无限循环的输出)
2.Runnable:
新建类实现Runnable接口抢鞋:
package com.google.demo;
public class BBRunnable implements Runnable{
private int shoe = 10;
@Override
public void run() {
while(true){
if(shoe>0){
System.out.println(Thread.currentThread().getName()+"抢到了第"+(shoe--)+"双鞋");
}
}
}
}
测试使用多线程抢鞋:
package com.google.demo;
public class Demo {
public static void main (String[] args){
BBRunnable bbRunnable = new BBRunnable();
new Thread(bbRunnable,"doinb").start();
new Thread(bbRunnable,"crisp").start();
new Thread(bbRunnable,"gimgong").start();
}
}
输出:
gimgong抢到了第8双鞋
crisp抢到了第9双鞋
doinb抢到了第10双鞋
crisp抢到了第6双鞋
gimgong抢到了第7双鞋
crisp抢到了第4双鞋
doinb抢到了第5双鞋
crisp抢到了第2双鞋
gimgong抢到了第3双鞋
doinb抢到了第1双鞋
3. Daemon:
程序定义了一个守护线程,并且该守护线程将一直进行信息输出,但是通过执行的结果可以发现,当用户线程消失后守护线程也同时结束。
定义一个Daemon类:
package com.google.demo;
public class DaemonTest implements Runnable{
@Override
public void run() {
System.out.println("保护保护保护保护保护保护");
}
}
测试守护线程:
package com.google.demo;
public class Demo {
public static void main (String[] args){
DaemonTest daemonTest = new DaemonTest();
BBRunnable bbRunnable = new BBRunnable();
Thread daeThread = new Thread(daemonTest);
daeThread.start();
new Thread(bbRunnable,"doinb").start();
new Thread(bbRunnable,"doinb").start();
}
}
三、问题的引入:
1.使用runnable抢鞋发生的问题:
抢鞋线程:
package com.google.demo;
public class BBRunnable implements Runnable{
private int shoe = 10;
@Override
public void run() {
while(true){
if(shoe>0){
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"抢到了第"+ (shoe--)+"双鞋");
}
}
}
}
测试线程:
public class Demo {
public static void main (String[] args){
BBRunnable bbRunnable = new BBRunnable();
new Thread(bbRunnable,"doinb").start();
new Thread(bbRunnable,"lwx").start();
}
}
结果:
doinb抢到了第9双鞋
lwx抢到了第10双鞋
lwx抢到了第8双鞋
doinb抢到了第8双鞋
lwx抢到了第7双鞋
doinb抢到了第6双鞋
lwx抢到了第5双鞋
doinb抢到了第5双鞋
lwx抢到了第4双鞋
doinb抢到了第4双鞋
doinb抢到了第3双鞋
lwx抢到了第3双鞋
lwx抢到了第2双鞋
doinb抢到了第2双鞋
lwx抢到了第1双鞋
doinb抢到了第1双鞋
运行速度快时,导致线程不同步。
2.Synchronized的提出:
synchronized是Java中的关键字,是一种同步锁。
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
package com.google.demo;
public class BBRunnable implements Runnable{
private int shoe = 10;
Object lock = new Object();
@Override
public void run() {
while (true) {
// 同步代码块
synchronized (lock) {
if (shoe > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +
"抢到了第" + (shoe--) + "双鞋");
}
}
}
}
}
3.Synchronized的优化:
ReentrantLock(同步锁)方法:
package com.google.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BBRunnable implements Runnable{
private int shoe = 10;
// 同步锁
Lock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true) {
reentrantLock.lock();
if (shoe > 0) {
try {
// 线程休眠
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +
"抢到了第" + (shoe--) + "双鞋");
}
reentrantLock.unlock();
}
}
}
四、线程的调度:
每一个线程的有限使用权都是系统随机分配人人平等的,谁先分配到谁先用。
更改调度优先级(无法达到完全优先):
package com.google.demo;
public class Demo {
public static void main (String[] args){
Thread minThread = new Thread((Runnable) new MinThread(),"minThread");
Thread maxThread = new Thread((Runnable) new MaxThread(),"maxThread");
maxThread.setPriority(Thread.MIN_PRIORITY);
minThread.setPriority(Thread.MAX_PRIORITY);
minThread.start();
maxThread.start();
}
}
线程插队:Thread.join()
package com.google.demo;
public class Demo {
public static void main (String[] args) throws InterruptedException {
Thread bbThread = new Thread(new BBThread(),"bbThread");
bbThread.start();
// 线程插队
for (int i = 1; i <8 ; i++) {
System.out.println(Thread.currentThread().getName());
if(i == 5){
bbThread.join();
}
}
}
}
输出:
main
main
main
main
main
bbThread
bbThread
bbThread
bbThread
main
main
线程调度:Thread.yield()
package com.google.demo;
public class BBThread extends Thread{
@Override
public void run() {
for(int i = 1;i<8;i++){
if(i==5) {
System.out.println(Thread.currentThread().getName()+"========线程开始让步");
Thread.yield();
}
System.out.println(Thread.currentThread().getName());
}
}
}
// 输出到5次开始让步,程序太快导致先让步后显示
输出:
11111111
11111111
11111111
11111111
22222222
22222222
22222222
22222222
11111111========线程开始让步
22222222========线程开始让步
22222222
22222222
22222222
11111111
11111111
11111111
五、线程状态:
斗地主——新建;
开始游戏——可运行;
抢地主——抢锁;
地主先出牌——阻塞;
等待——另外两位玩家等待出牌;
计时等待——20秒出牌时间;
终止——游戏结束;
线程的通信:notify
新建Condom对象:
package com.google.demo;
public class Condom {
public boolean isStatus = false;
}
新建用户线程类:
package com.google.demo;
public class Customer extends Thread{
private Condom condom = new Condom();
public Customer(Condom condom){
this.condom = condom;
}
@Override
public void run(){
while(true){
synchronized (condom){
if(condom.isStatus ==false) {
try {
condom.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condom.isStatus = false;
System.out.println(Thread.currentThread().getName()+"买完了铁子们");
condom.notify();
}
}
}
}
新建生产者线程类:
package com.google.demo;
public class Producer extends Thread {
private Condom condom = new Condom();
public Producer(Condom condom){
this.condom = condom;
}
@Override
public void run() {
while (true) {
synchronized (condom) {
if (condom.isStatus == true) {
try {
condom.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condom.isStatus = true;
System.out.println(Thread.currentThread().getName() + "生产出来了铁子们");
condom.notify();
}
}
}
}
测试效果:
package com.google.demo;
public class Demo {
public static void main (String[] args) throws InterruptedException {
Condom condom = new Condom();
Producer producer = new Producer(condom);
Customer customer = new Customer(condom);
producer.start();
customer.start();
}
}
输出:
Thread-0生产出来了铁子们
Thread-1买完了铁子们
Thread-0生产出来了铁子们
Thread-1买完了铁子们
Thread-0生产出来了铁子们
notify():
notify() 方法随机唤醒对象的等待池中的一个线程,进入锁池;
notifyAll() 唤醒对象的等待池中的所有线程,进入锁池。