Java 多线程 1
程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;
进程:执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位;
一个进程中可以包含若干个线程,一个进程至少有一个线程。
线程是CPU调度和执行的单位。
线程就是独立的执行路径
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
main()可以称为主线程,为系统的入口,用于执行整个程序;
线程创建
.三种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callback接口
继承Thread类
- 自定义类继承Thread类;
- 重写**run()**方法,编写线程执行体;
- 创建线程对象,调用start()方法启动线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class UThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("当前是线程: "+i); } }
public static void main(String[] args) { System.out.println("程序开始\n-----------------");
UThread1 uThread1 = new UThread1(); uThread1.start();
for (int i = 0; i < 200; i++) { System.out.println("这里是主线程:"+i); } } }
|
实现Runnable接口
.对于两种线程创建方式,建议使用”实现Runnable接口”方式实现多线程
目的
避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class RunnableImpl implements Runnable{ int id; int rest;
public RunnableImpl(int id, int rest) { this.id = id; this.rest = rest; }
@Override public void run() { while(rest>0){ System.out.println("我是"+id+" 剩余:"+rest+"次数"); rest--; } } }
|
使用
1 2 3 4 5 6
| public class Test { public static void main(String[] args) { Thread t1 = new Thread(new RunnableImpl(1,20)); t1.start(); } }
|
其他
线程开启不一定立即执行,由CPU调度执行
Callable方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Test implements Callable<Boolean> { int id; @Override public Boolean call() throws Exception { System.out.println(id); return Math.random()>0.5; } public Test(int id){ this.id = id; } public static void main(String[] args) { Test t1 = new Test(1);
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> rs1 = ser.submit(t1);
try { System.out.println("1. "+rs1.get()); }catch (Exception e){ System.out.println(e); } ser.shutdownNow();
} }
|
静态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class StaticProxy { public static void main(String[] args) { Restaurant restaurant = new Restaurant(new You()); restaurant.startEat(); } }
interface Eat{ void startEat(); }
class You implements Eat{ @Override public void startEat() { System.out.println("开始吃饭,超开心"); } }
class Restaurant implements Eat{ private Eat target; public Restaurant(Eat target){ this.target = target; } @Override public void startEat() { before(); target.startEat(); after(); }
private void after(){ System.out.println("吃完离开 Leave"); } private void before(){ System.out.println("烤Food"); } }
|
Lambda 表达式
函数式接口
- 任何接口,如果只包含一个抽象方法,则是一个函数式接口
- 对于函数式接口,可以通过lambda表达式来创建该接口的对象
匿名内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Test { public static void main(String[] args) { Eat lunch = new Eat() { @Override public void startEat() { System.out.println("吃午饭"); } }; lunch.startEat(); } }
interface Eat{ void startEat(); }
|
lambda表达式使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Test { public static void main(String[] args) { Eat lunch = ()->{ System.out.println("吃午饭"); }; lunch.startEat(); } }
interface Eat{ void startEat(); }
|
代参Lambda表达式
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Test { public static void main(String[] args) { Callback callback = (a)->{ System.out.println("传入参数为:"+a); return ++a; }; System.out.println(callback.call(123)); } }
interface Callback{ int call(int t); }
|
可以简化的内容
- 简化括号:若参数只有一个,则可以简化括号
- 简化花括号: 若代码只有一行,这可以简化花括号
简化括号
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Test { public static void main(String[] args) { Callback callback = a->{ System.out.println("传入参数为:"+a); return ++a; }; System.out.println(callback.call(123)); } }
interface Callback{ int call(int t); }
|
简化花括号
1 2 3 4 5 6 7 8 9 10 11
| public class Test { public static void main(String[] args) { Callback callback = t -> System.out.println(++t); callback.call(123); } }
interface Callback{ void call(int t); }
|
线程的一些方法
方法名 |
功能 |
setPriority(int newPriority) |
更改线程优先级 |
sleep(long millis) |
在指定的毫秒数内使正在执行的线程休眠 |
join() |
等待线程终止 |
yield() |
暂停当前线程,执行其他线程 |
interrupt() |
中断线程,一般不用 |
isAlive() |
测试线程是否处于活跃状态 |
停止线程
不推荐使用JDK提供的stop()和destroy()
推荐让线程自己停下来,建议使用一个标志位进行终止变量,当flag=false,则终止线程运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class YThread extends Thread{ boolean flag=true; @Override public void run() { while (flag){ System.out.println("Thread running..."); } } public void requestStop(){ flag = false; } } public class ThreadStop { public static void main(String[] args) { YThread yThread = new YThread(); yThread.start();
for (int i = 0; i < 100; i++) { System.out.println("Main "+i); if(i==60){ yThread.requestStop(); } } } }
|
线程休眠
.每个对象都有一个锁,sleep不会释放锁
Thread.sleep(毫秒)
线程礼让
.礼让不一定成功,看CPU心情调度
Thread.yield();
线程强制执行 join
.少用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class Test { public static void main(String[] args) { Thread thread = new Thread(new ARun(),"vip"); thread.start();
for (int i = 0; i < 100; i++) { if(i==20){ try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("main "+i); } } } class ARun implements Runnable{
@Override public void run() { for (int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"执行到"+i); } } }
|
获取线程状态
Thread.getState();
线程状态
Thread.State |
表达 |
NEW |
尚未启动的线程 |
RUNNABLE |
在Java虚拟机中执行的线程处于此状态 |
BLOCKED |
被阻塞的线程 |
WAITING |
等待另一个线程执行特定状态的状态 |
TIMED_WAITING |
等待另一个线程执行动作达到指定等待时间的状态 |
TERMINATED |
已退出的线程处于此状态 |
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class Test { public static void main(String[] args) { Thread thread = new Thread(new ARun()); thread.start(); System.out.println("线程开始"); while (thread.getState()!= State.TERMINATED){ System.out.println(thread.getState()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(thread.getState());
} } class ARun implements Runnable{
@Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("/////"); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
线程优先级
线程优先级用数字表示,范围1~10
常量名 |
值 |
介绍 |
Thread.MIN_PRIORITY |
1 |
最低 |
Thread.MAX_PRIORITY |
10 |
最高 |
Thread.NORM_PRIORITY |
5 |
默认 |
改变优先级/获取优先级
setPriority(int p)
getPriority()
守护线程
thread.setDaemon(true);//false为用户线程,true为守护线程。默认为false。
虚拟机必须保证用户线程执行完毕,不用等待守护线程执行完毕。
比如:后台记录操作日志,监控内存,垃圾回收等等。
线程同步
多个线程访问同一个对象,为了保障数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
存在问题:
- 一个线程持有锁会导致其他所有需要锁的线程挂起;
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
线程同步安全 synchronized
synchronized有两种用法:synchronized方法和synchronized块。
同步方法public synchronized void method(int args){}
synchronized方法必须获得调用该方法的对象的锁才能执行,否则线程会阻塞。方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。
方法里面需要修改的内容需要锁。只读的代码不需要锁。
缺陷:若将一个大的方法申明为synchronized将会影响效率。
synchronized锁住的对象
- 修饰实例方法:作用于当前实例加锁。
- 修饰静态方法:作用于当前类对象加锁(锁住Class对象,然后,该对象的静态方法都得等待)。
- 修饰代码块:指定加锁对象,对给定对象加锁。
加锁之后,锁只对有synchronized修饰的方法有效。
同步块
同步块:synchronized(Obj){}
Obj称为同步监视器。
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器。
Obj一般是同步块内变化的量。(增删改的对象)
CopyOnWriteArrayList
安全的ArrayList
死锁
产生死锁必要条件
- 互斥条件:一个资源每次只能被一个进程使用;
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
- 不剥夺条件:进程进程已获得的资源,在未使用完之前,不能强行剥夺;
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
Lock锁
java.util.concurrent.locks.Lock
接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁。
ReentrantLock
类实现了Lock,拥有与synchronized
相同的并发性和内存语义。
ReentrantLock
即可重入的互斥锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class Test { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"a").start(); new Thread(buyTicket,"b").start(); new Thread(buyTicket,"c").start();
} } class BuyTicket implements Runnable{ int ticket=10; private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ lock.lock(); try{ if (ticket>0){ Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" "+ticket--); }else{ break; } }catch (InterruptedException e){ System.out.println(e); }finally { lock.unlock(); } } } }
|