一、线程与进程
1.进程:指一个内存中运行的应用程序,每个进程都有一个独立的内存空间;
2.线程:指进程的一个执行路径,多个线程之间可以通信,自由切换,并发执行;一个线程最少有一个线程;
3.线程分为用户线程和守护线程;
4.线程调度:
(1)分时调度:所有线程轮流使用CPU,平均使用时间;
(2)抢占式调度:优先让优先级高的线程使用CPU,优先级相同,则随机选择;Java为抢占式调度。
二、同步与异步
(1)同步:线程安全
排队执行,效率低但安全;
(2)异步:线程不安全
同时执行,效率高但不安全。
三、并发和并行
(1)并发:
指在同一时间段内发生;
例如在一分钟内的交易数量等;
(2)并行:
指在同一时刻发生;
四、创建线程:
1.继承Thread;
(1)在继承的类中,写run( )方法,描述线程要做的功能;
(2)当需要开启一个新线程来执行run( )中的内容,则在调用的方法中,创建(1)中的类的对象,然后使用start( )方法来开启一个新线程,然后执行run( )中的内容;
2.实现接口Runnable;
(1)过程:
1.创建一个实现了Runnable接口的类1,同时实现了抽象方法run( );
2.在调用的方法中,创建类1的对象,在创建Thread类的对象的构造方法的参数中,传入类1的对象;
3.然后Thread类的对象使用start( )方法开启一个新线程来执行类1中的run( )。
public class Thread1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable());
t1.start();
for(int i = 0;i<=5;i++){
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+":"+i);
}
//打一个中断标记;
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("发现线程中断标记,终止线程");
return;
}
}
}
}
}
(2)优势:
1.灵活性强,在给Thread类的构造方法传参时,可以传多种,更适合实现多线程执行多任务,传参可以理解为分配任务;
2.可以避免单继承的局限性,因为Java中不允许多继承,但支持多实现,可以实现多个接口;
3.任务和线程的创建时分离的,提高了程序的健壮性;
4.Runnable,可在线程池中使用;
五、Thread类
1.四种常用构造方法;
Thread(String name):name为线程名称;
2.三个静态常量:优先级设置;
MAX_PRIORITY,最高
MIN_PRIORITY,最小
NORM__PRIORITY,默认
3.常用方法:
(1)start( );
结束线程,应使用变量标记法,不能使用过时方法stop( ),因为在流中会导致无法正常关闭流。
(2)getPriority 和 setPriority(优先级静态常量);
(3)getId( )
(4)getName 和 setName(String)
(5)static Thread currentThread( ):
Thread类的静态方法,获得当前线程对象;
(6)static void sleep(int 毫秒数):
Thread类的静态方法,线程休眠;
(7)setDaemon(boolean):设置守护线程;
参数为true则设为守护线程;
线程分为 daemon线程(即守护线程)和用户线程;
直接创建的都是用户线程;
守护线程:当最后一个用户线程结束,则所有守护线程结束,代码如下;
public class Thread2 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Thread1.MyRunnable(),"守护线程");
//设置为守护线程;
t1.setDaemon(true);
t1.start();
for(int i = 0;i<=5;i++){
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
六、线程阻塞:
所有比较耗费时间的操作都叫线程阻塞;
包括但不限于:IO文件操作,等待用户输入,等。
七、线程中断:
1.使用interrupt()方法,进行中断标记;
在线程中使用Wait(),sleep(…),等方法后会抛出InterruptedExprection异常,在sleep的try…catch块中使用return结束run( )方法,从而结束线程。
public class Thread1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable());
t1.start();
for(int i = 0;i<=5;i++){
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+":"+i);
}
//打一个中断标记;
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("发现线程中断标记,终止线程");
return;
}
}
}
}
}
八、线程安全1同步代码块
synchronized(锁对象)
1.锁对象可以是任何对象;
2.要保证线程安全和同步,要使用同一个锁对象;
public class Thread3 {
public static void main(String[] args) {
MyRunnable m1 = new MyRunnable();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
static class MyRunnable implements Runnable{
private int ticket = 18;
private Object o = new Object();
@Override
public void run() {
while(true){
//线程安全1,线程安全代码块;
synchronized (o){
if(ticket>0){
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+ticket);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
}
九、线程安全2同步方法:
1.加synchronized修饰方法;
2.方法中的锁对象为:
当是static方法,锁对象为:类名.class
不是static方法时,锁对象:this
3.当同时出现同步代码块和同步方法时,若锁对象相同,则每次只能执行其中一个;
4.当有多个相同static修饰情况的synchronized方法时,则每次只执行其中一个;
public class Thread4 {
public static void main(String[] args) {
MyRunnable m1 = new MyRunnable();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
static int[] asd(int[] a){
int[] b = new int[1];
b[1] = a.length;
return b;
}
static class MyRunnable implements Runnable{
private int ticket = 18;
private Object o = new Object();
@Override
public void run() {
while(true){
if(!isNot()){
break;
}
}
}
//线程安全2,安全方法修饰
public synchronized boolean isNot(){
if(ticket>0){
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+ticket);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}else{
return false;
}
}
}
}
十、线程安全3显示锁:
1.抽象类Lock的子类,ReentrantLock;
Lock l = new ReentrantLock( );
2.常用方法:
加锁:lock( ); 解锁:unlock( )
十一、公平锁
1.公平锁:
先来先到,按照代码从上到下的顺序执行;
在显示锁实例化时,添加true参数;
Lock l = new ReentrantLock(true);
2.Java中的三种锁机制默认都是非公平锁;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Thread5 {
//Lock,显示锁和公平锁
public static void main(String[] args) {
MyThread m1 = new MyThread();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
static class MyThread implements Runnable{
private int ticket = 18;
//公平锁
Lock l1 = new ReentrantLock(true);
@Override
public void run() {
while(true){
l1.lock();
if(ticket>0){
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+ticket);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
l1.unlock();
}else{
l1.unlock();
break;
}
}
}
}
}
十二、线程死锁
1.死锁:环环相扣,两个锁互相等待对方解锁才能继续执行;
2.避免方法:当有可能出现锁的情况时,只允许又一个锁即可;
public class Thread6 {
//死锁
public static void main(String[] args) {
Police p = new Police();
Crime c = new Crime();
Mythread m1 = new Mythread(p,c);
new Thread(m1).start();
c.say(p);
}
static class Mythread implements Runnable{
private Police p;
private Crime c;
public Mythread(Police p, Crime c) {
this.p = p;
this.c = c;
}
@Override
public void run() {
p.say(c);
}
}
static class Police{
public synchronized void say(Crime c){
System.out.println("警察:放了人质,我就放了你");
c.respond();
}
public synchronized void respond(){
System.out.println("人质救出,罪犯跑了");
}
}
static class Crime{
public synchronized void say(Police p){
System.out.println("罪犯:放了我,就放了人质");
p.respond();
}
public synchronized void respond(){
System.out.println("罪犯跑了,人质也放了");
}
}
}
十三、多线程通信问题:生产者和消费者
1.主要方法:
使用Object类的wait( ) 和 notifyAll( );
2.也可以使用公平锁来解决;
public class Thread8 {
//多线程通信
//生产者和消费者
public static void main(String[] args) {
Food f1 = new Food();
Cooker c = new Cooker(f1);
Waiter w = new Waiter(f1);
c.start();
w.start();
}
static class Cooker extends Thread{
private Food f;
public Cooker(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
f.setAll("牛排", "咸香");
} else {
f.setAll("螺蛳粉", "恶臭");
}
}
}
}
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
static class Food{
private String name;
private String taste;
private boolean flag = true;
public synchronized void setAll(String n, String t){
if(flag){
this.name = n;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = t;
flag = false;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(!flag){
System.out.println("服务员端走的菜的名称是:"+name+",口味是:"+taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
十四、线程的六种状态
十五、带返回值的线程:CallAble
1.先实现接口CallAbale<T>,实现方法<T> call( );
2.再创建类FutureTask<T>,传入第一步创建的CallAble对象;
3.new Thread(FutureTask对象);
其中在FutureTask对象有以下方法,实现主线程等待CallAble线程先执行:
1.常用方法:
IsDone( ),是否结束;
cancel(Boolean ),提前结束该线程;
get( ),获取call()的返回值,主线程会等待CallAble线程先执行,得到返回值后再继续执行主线程;
2.上述方法都会导致主线程等待。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Thread7 {
//CallAble线程
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable m1 = new MyCallable();
FutureTask<Integer> myCallableFutureTask = new FutureTask<Integer>(m1);
new Thread(myCallableFutureTask).start();
System.out.println(myCallableFutureTask.get());
}
static class MyCallable implements Callable<Integer> {
private int age = 18;
@Override
public Integer call() throws Exception {
return age;
}
}
}
十六、线程池:
ExecutorSerivce类。
因为创建和关闭线程的时间相比执行时间多得多,所以若多次创建关闭会浪费时间,所以创建大量线程时,使用线程池。
1.包含多个线程的容器;
2.结构上分为:线程数组和任务列表;
3.线程数组分为定长数组、可变数组;对于定长数组,任务较多时会出现排队的情况;且数组中靠前的线程优先执行任务;
4.线程池共分为以下四种;
十七、缓存线程池
-
不定长线程池;
-
当任务数量过多时,会自动对线程数组扩容,不会出现任务过多而排队的情况;当任务数量较少,会自动删除空闲的线程;
ExecutorSerivce e = Executors. newCachedThreadPool( );e.execute(new Runable);
-
注意:当有空闲线程时,会使用已有线程,不会新建线程,例子如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Pool1 {
//缓存线程池
public static void main(String[] args) {
ExecutorService e1 = Executors.newCachedThreadPool();
e1.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"111111");
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
e1.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"222222");
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
e1.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"333333");
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
十八、定长线程池
线程数量不变;
当有过多任务时,会出现排队的情况;
ExecutorSerivce e = Executors. newFixedThreadPool( 10 );
e.execute(new Runnable);
十九、单线程线程池
只有一个线程;
场景:当任务要求排队执行时;
ExecutorSerivce e = Executors. newSingleThreadPool();
e.execute(new Runnable);
二十、周期 定长 线程池
ScheduledExecutorSerivce e=Executors. newScheduledThreadPool(2);
1.定时执行一次:schedule( … ,…, … ),三个参数:
e.schedule(new Runnable, int 时长数字,TimeUnit.时间单位);
参数3是指定参数2的时间单位;
- scheduleAtFixedRate;四个参数:
e.scheduleAtFixedRate(new Runnable, 延迟时长(第一次执行在什么时间之后),周期时长,TimeUnit.时间单位);
参数4是指定参数3的时间单位;
二十一、Lambda表达式:
将匿名内部类的实现,只保留对应抽象方法的参数和方法体部分,参数和方法体之间使用 -> 相连;
public class Lambda {
public static void main(String[] args) {
//正常匿名内部类格式
ShowBook(new Book() {
@Override
public int page(int i) {
return i;
}
},100);
//Lambda格式
ShowBook((int i) -> {return i;},200);
}
static void ShowBook(Book b,int x){
int page = b.page(x);
System.out.println(page);
}
static interface Book{
int page(int i);
}
}