Java基础
线程
- 简介
程序:静态的代码
进程:程序动态的执行过程,产生,执行,消亡
线程:进程内最小的执行单位,动态执行的过程
进程有自己独立的内存
线程共享进程的资源,在进程内,每个线程可以完成独立的功能
进程和线程关系图如下:
- 线程创建的两种方式
2.1 继承Thread类:线程类
2.2 实现Runnable接口: 不是线程类
注意:线程代码要写到run()方法里
在主线程里启动线程start()方法,启动了才能执行
代码小示 ^ _ ^ :
package com.java.day12_3;
//继承Thread类的方式创建线程
class T1 extends Thread{
//线程完成功能代码要写到线程体方法中,run()方法
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("hello");
}
}
}
class T2 extends Thread{
//线程完成功能代码要写到线程体方法中,run()方法
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("Thread");
}
}
}
public class MyThread {
//主线程
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
t1.start();
t2.start();
}
}
package com.java.day13;
//实现Runnable接口的方式创建线程
//Runnable1并不是线程类
//Runnable是函数式接口,可以用lambda表达式
class Runnable1 implements Runnable{
@Override
public void run() {
for(int i = 1; i <= 10; i++) {
System.out.println(i);
}
}
}
public class Demo {
public static void main(String[] args) {
Runnable1 r = new Runnable1();
Thread t = new Thread(r);
t.start();
Thread t2 = new Thread(()->{//lambda表达式
for(int i = 1; i <= 10; i++) {
System.out.println("hello");
}
});
t2.start();
}
}
- 线程的常用方法
Thread.currentThread() ---------获取当前线程对象
getName() ---------获取当前线程名字
getId() --------获取当前线程id
isAlive() ---------判断当前线程是否还活着(能用)
package com.java.day13;
public class Demo3 {
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run() {
System.out.println(Thread.currentThread());//Thread[线程1,5,main] 名字,优先级,所处线程
System.out.println(getName());//线程1 名字
System.out.println(getId());//10 id
System.out.println(isAlive());//true 活着
}
};
t1.setName("线程1");
t1.start();
System.out.println(Thread.currentThread());//Thread[main,5,main]
System.out.println(Thread.currentThread().getName());//main
System.out.println(Thread.currentThread().getId());//1
System.out.println(Thread.currentThread().isAlive());//true
}
}
Thread.sleep(毫秒数);---------线程休眠多少毫秒
package com.java.day13;
public class Demo4 {
/**
* sleep(毫秒):线程休眠
* 如果休眠的过程中被唤醒,会抛出InterruptedException
*/
public static void main(String[] args) {
new Thread(()->{
System.out.println(Thread.currentThread().getName());
for(int i = 10; i >= 0; i--) {
if(i != 0) {
System.out.println(i);
}else {
System.out.println("过年了!");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"线程sleep").start();
}
}
join();---------当前线程等待其他线程先执行
package com.java.day13;
//模拟图片显示和图片下载
public class Demo5 {
/**
* join();
* 当前线程等待其他线程先执行
*/
public static void main(String[] args) {
//图片下载的线程
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("正在下载中");
for(int i = 0; i <= 100; i++) {
System.out.println(i+"%");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//图片显示的线程
Thread t2 = new Thread(()->{
//for(int i = 0; i <= 10; i++) {
// System.out.println("Thread-2:"+i);
// if(i == 3) {
// try {
// t1.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//}
System.out.println("图片显示");
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("图片显示成功");
});
t2.start();
t1.start();
}
}
- 守护线程
setDeamon(true);
创建出来的线程:用户线程(前台线程)和守护线程(后台线程)。
设置守护线程,要调用setDeamon(true);
守护线程和用户线程的用法和创建方式没有区别。
两者的区别:用户线程执行完毕,守护线程无条件停止执行。
package com.java.day13;
/*
* 守护线程
*/
public class Demo6 {
/**
* 模拟泰坦尼克号 you jump! I jump!
*/
public static void main(String[] args) {
Thread rose = new Thread() {
@Override
public void run() {
for(int i = 0; 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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
rose.start();
jeck.setDaemon(true);//在启动线程之前设置线程为守护线程
jeck.start();
}
}
- 线程的生命周期
从生到死的几个阶段:
新建状态:new Thread() 可以有两个选择:完成使命start(),直接消亡
就绪状态:调用start()
阻塞状态:调用sleep()、wait()、等待加载资源等,让出CPU资源
运行状态:执行run()
消亡:执行完run()中的语句
注意:调用sleep醒了之后,重新处于就绪状态 - 线程的优先级
不同优先级:高优先级先执行,低优先级后执行(高优先级具备了先执行的权利,但不一定先执行)。
同一优先级:先到先服务。
线程优先级的规范:系统默认线程的优先级为中等优先级;人为设置线程的优先级。
人为设置优先级:setPriority(常量值);常量值有MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY
package com.java.day13;
public class Demo7 {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
for(int i = 0; i <= 10; i++) {
System.out.println("t1");
}
});
Thread t2 = new Thread(()->{
for(int i = 0; i <= 10; i++) {
System.out.println("t2");
}
});
Thread t3 = new Thread(()->{
for(int i = 0; i <= 10; i++) {
System.out.println("t3");
}
});
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
-
线程同步
当多个线程操作一个共享变量时,可能会造成共享变量的不安全性(如共享变量为1且该变量应该大于等于0,两个线程对变量进行减1操作,第一个线程减1后还没来得及改为0,第二个线程以为还是1也进行了减1操作,最终这个变量变成了-1)。为了确保共享变量的安全性,使用同步机制保证共享变量的安全性。
同步机制:当一个线程改变共享变量值的时候,其他线程不能使用共享变量,当线程计算完成,返回变量值之后,其他线程才可以使用共享变量。
使用 synchronized关键字实现线程同步,下面是两种方式:
第一种:
同步块 synchronized(锁对象){
共享变量相关的业务代码(运算)
}
第二种:
在方法上添加synchronized,用此关键字修饰的方法叫同步方法。
package com.java.day13;
/**
* 模拟窗口买票
* 一个窗口即为一个线程
*/
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 = 3;
public static void main(String[] args) {
TicketThread t1 = new TicketThread();
TicketThread t2 = new TicketThread();
TicketThread t3 = new TicketThread();
TicketThread t4 = new TicketThread();
TicketThread t5 = new TicketThread();
TicketThread t6 = new TicketThread();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
package com.java.day13;
/**
* 模拟两个人往一个银行账户存钱
*/
class Bank{
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()+"存钱"+money);
System.out.println("账户余额为"+this.money);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Demo10 {
public static void main(String[] args) {
Bank bank = new Bank();
Thread t1 = new Thread(()->{
bank.save(1000);
});
Thread t2 = new Thread(()->{
bank.save(1000);
});
t1.start();
t2.start();
}
}
- 同步线程中的通信
wait();
notify();
notifyAll();
package com.java.day13;
/**
* 两个线程交替打印1-10
* wait();
* notify();
*/
class CountThread implements Runnable{
int i = 1;
@Override
public void run() {
synchronized (this) {
while(i <= 10) {
notify();//唤醒wait的线程
System.out.println(Thread.currentThread().getName()+":"+i);
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
if(i > 10)
break;
wait();//等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public class Demo11 {
public static void main(String[] args) {
CountThread ct = new CountThread();
Thread t1 = new Thread(ct);
Thread t2 = new Thread(ct);
t1.start();
t2.start();
}
}
- sleep和wait的区别
sleep | wait |
---|---|
是线程类的方法 | 是object的方法 |
不是释放锁 | 是释放锁 |
自动醒 | 必须使用notify,notifyAll唤醒 |
- 线程的死锁
- 线程池
频繁的创建线程,需要耗费时间和内存,使用线程池可以使线程重用,还可以管理线程。
ExecutorService t = Executors.newCashedThreadPool();
t.execute();
ExecutorService t = Executors.newFixedThreadPool(3);//指定线程池的线程数
t.shutdown();//不关闭正在执行的线程
t.shutdownNow();//不管线程执没执行完,运行到这条语句就关闭线程池 - 练习
开发者 - 消费者
当有产品时(即产品数>0),消费者可以买产品,当没有产品时,告诉生产者,让他生产产品。
package com.java.day13;
/**
* 商品管理者
*/
public class Manager {
int protect = 10;
//生产商品
public synchronized void makeProtect() {
if(protect <= 0) {
protect++;
System.out.println(Thread.currentThread().getName()+"生产了一件商品");
}else{
notifyAll();
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//获得商品
public synchronized void getProtect() {
if(protect > 0) {
protect--;
System.out.println(Thread.currentThread().getName()+"买商品");
}else {
notifyAll();
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package com.java.day13;
/**
* 商品生产者
*/
public class Producer implements Runnable{
private Manager manager;
public Producer(Manager manager) {
this.manager = manager;
}
@Override
public void run() {
while(true) {
manager.makeProtect();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
package com.java.day13;
/**
* 消费者
*/
public class Consumer implements Runnable{
private Manager manager;
public Consumer(Manager manager) {
this.manager = manager;
}
@Override
public void run() {
while(true) {
manager.getProtect();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package com.java.day13;
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
Manager manager = new Manager();
Thread t1 = new Thread(new Producer(manager));
Thread t2 = new Thread(new Consumer(manager));
t2.start();
t1.start();
}
}