什么是多线程?
可以简单这么理解:单线程的程序就像只雇佣一个服务员的餐厅,他必须做完一件事情后才可以做下一件事情;而多线程的程序则如同雇佣多个服务员的餐厅,他们可以同时做多件事情。
程序:一段静态的代码
进程:静态的代码的动态执行过程,产生,执行,消亡
线程:进程内最小的执行单位,动态执行的过程
进程有自己独立的内存
线程共享进程的资源,在进程内,每个线程可以完成独立的功能
线程创建的两种方式:
1.继承 Thread 类: 线程
class T extends Thread{
//线程完成的功能代码要写到线程体中
public void run() {
for(int i=0;i<100;i++) {
System.out.println("hello");
}
}
}
class T2 extends Thread{
public void run() {
for(int i=0;i<100;i++) {
System.out.println("thread");
}
}
}
public class MyThread {
//main() 主线程,可以管理其他线程
public static void main(String[] args) {
T t1=new T();
T2 t2=new T2();
//启动线程,start方法
t1.start();
t2.start();
}
}
2.实现 Runnable 接口(是一个函数式接口):不是线程
class Runnable1 implements Runnable{
@Override
//注意:run() 没有异常抛出,参数列表为空
public void run() {
for(int i=0;i<10;i++) {
System.out.println(i);
}
}
}
public class Demo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Runnable1 r=new Runnable1();
Thread t=new Thread(r);
t.start();
//匿名函数的形式
Thread t2=new Thread(()->{
for(int i=0;i<10;i++) {
System.out.println("hello");
}
}
);
t2.start();
}
}
练习:用两种方式创建两个线程,打印10以内的偶数
class T3 extends Thread{
public void run() {
for(int i=0;i<=10;i++) {
if(i%2==0) {
System.out.println(i);
}
}
}
}
public class MyThread {
public static void main(String[] args) {
T3 t3=new T3();
t3.start();
}
}
class Runnable2 implements Runnable{
@Override
public void run() {
for(int i=0;i<=10;i++) {
if(i%2==0) {
System.out.println(i);
}
}
}
}
public class Demo {
public static void main(String[] args) {
Runnable2 r2=new Runnable2();
Thread t3=new Thread(r2);
t3.start();
}
}
线程类的构造方法
Thread()
//name - 新线程的名称。
Thread(String name)
Thread(Runnable r)
Thread(Runnable r,String name)
Thread t=new Thread() {
//new Thread() {
public void run() {
System.out.println(Thread.currentThread());
//继承Thread类,可使用方法
System.out.println(getName());
System.out.println(getId());
System.out.println(isAlive());
}
//}.start();
};
t.setName("线程1");
t.start();
结果为:
Thread[线程1,5,main]
线程1
13
true
常用方法
public static void main(String[] args) {
//返回正在运行的线程对象
Thread t=Thread.currentThread();
System.out.println(t);
//返回线程的名称
System.out.println(t.getName());
//返回线程的唯一id
System.out.println(t.getId());
// 判断线程是否可用
System.out.println(t.isAlive());
}
结果:
Thread[main,5,main]
main
1
true
sleep方法:线程休眠的方法
Thread.sleep()
/**
* sleep(毫秒)
* @author 86132
*
*/
public class Demo3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(()->{
//获取正在运行的线程的名字
System.out.println(Thread.currentThread().getName());
for(int i=10;i>=0;i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
,"线程sleep").start();
}
}
练习:
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(()->{
System.out.println("过年了,倒计时开始");
for(int i=10;i>=0;i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
).start();
}
join方法:等待线程执行完毕
Thread t=new Thread() {
public void run() {
for(int i=1;i<=10;i++) {
System.out.println("Thread-1"+":"+i);
}
}
};
Thread t2=new Thread(
()->{
for(int i=1;i<=10;i++) {
System.out.println("Thread-2"+":"+i);
if(i==3) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
t2.start();
结果为:
Thread-2:1
Thread-2:2
Thread-2:3
Thread-1:1
Thread-1:2
Thread-1:3
Thread-1:4
Thread-1:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
Thread-1:10
Thread-2:4
Thread-2:5
Thread-2:6
Thread-2:7
Thread-2:8
Thread-2:9
Thread-2:10
注意:线程执行的先后顺序是不一定的
练习:模拟图片显示,图片下载
Thread t3=new Thread() {
public void run() {
for(int i=0;i<=100;i++) {
System.out.println("图片下载进度:"+"%"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("图片下载完成");
}
};
Thread t4=new Thread(()->{
System.out.println("图片显示");
System.out.println("图片在加载");
try {
t3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("图片显示成功");
});
t3.start();
t4.start();
守护线程
setDeamon(true);
创建出来的线程:用户线程(前台线程)和守护线程(后台线程)
创建的线程都是用户线程
设置守护线程,调用setDeamon(true);
用法和创建方式没有区别
区别:用户线程执行完毕,守护线程无条件停止执行
rose ->用户线程,3次
jeck ->守护线程,while(true)
public static void main(String[] args) {
Thread rose=new Thread() {
public void run() {
for(int i=1;i<=3;i++) {
System.out.println("一起跳");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("扑通。。。。。。");
}
};
Thread jeck=new Thread(()->{
while(true) {
System.out.println("你先跳,我再跳");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
rose.start();
jeck.setDaemon(true);
jeck.start();
}
线程的生命周期(从生到死)
新建状态:new Thread()
就绪状态:start()
阻塞状态:sleep(),wait(),io,解除,重新排队
运行状态:run(),自动执行的
消亡:执行完run方法的语句
线程的优先级
不同优先级:高优先级先执行,低优先级后执行
同一优先级:先到先服务
线程的优先级的规范
系统自动分配:5
人为设置线程的优先级:1,5,10
注意:高优先级有优先执行的权利,但是不一定先执行
内存不够用时,先杀死的是低优先级
Thread t1=new Thread(()->{
for(int i=1;i<=10;i++) {
System.out.println("t1");
}
});
Thread t2=new Thread(()->{
for(int i=1;i<=10;i++) {
System.out.println("t2");
}
});
Thread t3=new Thread(()->{
for(int i=1;i<=10;i++) {
System.out.println("t3");
}
});
t3.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t1.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
线程同步
多个线程操作一个共享变量时,可能会导致共享变量的不完整(不安全)
为了保证共享变量的安全,使用同步机制保证共享变量的安全性
同步机制:当一个线程改变共享变量值的时候,其他线程不能使用共享变量,当线程计算完成,返回变量值之后,其他线程才可以使用共享变量
使用synchronized关键字实现线程同步
同步块synchronized
synchronized(锁对象){
共享变量相关的业务代码(运算)
}
注意:锁对象只能是一个
class TicketThread extends Thread{
public void run() {
while(true) {
synchronized(Demo8.class) {
if(Demo8.ticket<=0) {
try {
throw new Exception("票卖完了");
} catch (Exception e) {
System.out.println(e.getMessage());
break;
}
}else {
Demo8.ticket--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩"+Demo8.ticket+"张");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public class Demo8 {
static int ticket=10;
public static void main(String[] args) {
// TODO Auto-generated method stub
TicketThread t1=new TicketThread();
TicketThread t2=new TicketThread();
TicketThread t3=new TicketThread();
t1.start();
t2.start();
t3.start();
}
}
练习:
线程同步,商场买衣服
商场挑衣服,异步
试衣间只有一个,试衣服是同步
class Shopping{
public void test() {
System.out.println(Thread.currentThread().getName()+"挑衣服");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
synchronized(this) {
System.out.println(Thread.currentThread().getName()+"试衣服");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Demo11 {
public static void main(String[] args) {
Shopping s=new Shopping();
Thread t1=new Thread(()-> {
s.test();
});
Thread t2=new Thread(()-> {
s.test();
});
t1.start();
t2.start();
}
}
在方法上synchronized,用此关键字修饰的方法叫同步方法
银行存钱: 一个账号,两个人存钱
class Bank{
private int money=100;
public synchronized void save(int money) {
for(int i=0;i<3;i++) {
this.money+=money;
System.out.println(Thread.currentThread().getName()+"余额为"+this.money);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Demo9 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Bank bank=new Bank();
Thread t1=new Thread(()->{
bank.save(1000);
});
Thread t2=new Thread(()->{
bank.save(1000);
});
t1.start();
t2.start();
}
}
结果:
Thread-0余额为1100
Thread-0余额为2100
Thread-0余额为3100
Thread-1余额为4100
Thread-1余额为5100
Thread-1余额为6100
同步线程之间的通信:
解决临界资源问题
wait():
notify():
notifyAll():
使用两个线程交替打印10
class NumThread implements Runnable{
int i=1;
@Override
public void run() {
// TODO Auto-generated method stub
synchronized(this) {
while(i<=10) {
notify(); //唤醒wait的线程
System.out.println(Thread.currentThread().getName()+":"+i);
i++;
try {
if(i>10)
break;
wait(); //使当前线程等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public class Demo10 {
public static void main(String[] args) {
// TODO Auto-generated method stub
NumThread num=new NumThread();
new Thread(num).start();
new Thread(num).start();
}
}
结果为:
Thread-0:1
Thread-1:2
Thread-0:3
Thread-1:4
Thread-0:5
Thread-1:6
Thread-0:7
Thread-1:8
Thread-0:9
Thread-1:10
wait和sleep的区别
wait在同步块/同步方法中释放锁
银行取钱问题:sleep()方法执行之后,没有交给别的线程
sleep线程类的方法 wait是Object类的方法
sleep方法不是释放锁 wait是释放锁
sleep方法自动醒 wait必须使用notify,notifyAll唤醒
练习:生产者和消费者
class Clerk{
private int product = 0;
public synchronized void addProduct() {
// TODO Auto-generated method stub
if(product>=20) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
product++;
System.out.println("生产者生产了第"+product+"个产品");
notifyAll();
}
}
public synchronized void getProduct() {
// TODO Auto-generated method stub
if(product<=0){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
System.out.println("消费者取走了第"+product+"个产品");
product--;
notifyAll();
}
}
}
class Productor implements Runnable{
private Clerk c;
public Productor(Clerk c) {
// TODO Auto-generated constructor stub
this.c=c;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("生产者生产产品");
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
c.addProduct();
}
}
}
class Consumer implements Runnable{
private Clerk c;
public Consumer(Clerk c) {
// TODO Auto-generated constructor stub
this.c=c;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("消费者消费产品");
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
c.getProduct();
}
}
}
public class DemoProduct {
public static void main(String[] args) {
// TODO Auto-generated method stub
Clerk c=new Clerk();
Thread t1=new Thread(new Productor(c));
Thread t2=new Thread(new Consumer(c));
t1.start();
t2.start();
}
}
练习1://12A 34B 56C 78C … 4950Y 5152Z
class NumThread implements Runnable{
int i=1;
char c='A';
public void run() {
synchronized(this) {
while(i<=52) {
notify();
String a=i+""+(i+1);
i+=2;
if(c<='Z') {
notify();
System.out.println(a+""+c);
c+=1;
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public class DemoTest1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
NumThread num=new NumThread();
new Thread(num).start();
new Thread(num).start();
}
}
练习二:
/* num=1 ++
0:1,2,3,4,5, 1 16 31 if(num/5%3 == 0)
1:6,7,8,9,10, 6 21 36 %3 == 1
2:11,12,13,14,15, 11 26 41 %3 == 2
0:16,17,18,19,20,
1:21,22,23,24,25,
2:26,27,28,29,30,
0:31,32,33,34,35,
1:36,37,38,39,40,
2:41,42,43,44,45,
…
75
*/
class OneThread implements Runnable{
static int a=1;
public void run() {
synchronized(this) {
for(int num=a;num<=46;num++) {
notify();
if((num/5)%3 ==0) {
if(num==1) {
System.out.print(0+":"+num+",");
}else {
System.out.print(num+",");}
}
if((num/5)%3 ==1) {
System.out.print(num+",");
}
if((num/5)%3 ==2) {
System.out.print(num+",");
}
if(num%5==0) {
System.out.print("\n"+(num/5)%3+":");
}
if(num==46) {
a=(num+1);
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public class DemoTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
OneThread num=new OneThread();
new Thread(num).start();
new Thread(num).start();
new Thread(num).start();
}
}
线程的死锁
StringBuffer str1 = new StringBuffer();
StringBuffer str2 = new StringBuffer();
synchronized (str1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (str2){
System.out.println("str1");
System.out.println("str2");
}
}
synchronized (str2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (str1){
System.out.println("str1");
System.out.println("str2");
}
}
线程池
为什么使用线程池?
频繁的创建线程,需要耗费时间和内存
使用线程池的好处:
-
管理线程
-
使线程重用
package cn.tedu.demo.thread;
import java.text.DateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo {
//创建一个线程池,如果线程池中的线程数量过大,它可以有效的回收多余的线程,
// 如果线程数不足,那么它可以创建新的线程。
public static void test1() throws Exception {
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
final int index = i;
Thread.sleep(1000);
//完成5个任务
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " " + index);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
//指定线程池中的线程数,线程数是可以进行控制的
public static void test2() throws InterruptedException {
// ExecutorService threadPool = Executors.newFixedThreadPool(1);
ExecutorService threadPool =
Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
final int index = i;
threadPool.execute(() -> {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + index);
});
}
threadPool.shutdown();//不关闭正在执行的线程
//threadPool.shutdownNow();//不管线程是否执行完毕,立即停止执行
System.out.println("关闭线程!");
}
//线程池支持定时周期性任务执行
public static void test3() {
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
threadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
long start = new Date().getTime();
System.out.println("scheduleAtFixedRate 开始执行时间:" +
DateFormat.getTimeInstance().format(new Date()));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = new Date().getTime();
System.out.println("scheduleAtFixedRate 执行花费时间=" + (end - start) / 1000 + "m");
System.out.println("scheduleAtFixedRate 执行完成时间:" + DateFormat.getTimeInstance().format(new Date()));
System.out.println("======================================");
}
}, 1, 5, TimeUnit.SECONDS);
}
//单线程池,至始至终都由一个线程来执行
public static void test4() {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int index = i;
threadPool.execute(() -> {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + index);
});
}
// threadPool.shutdown();
}
public static void main(String[] args) throws Exception {
test2();
}
}