Day6 Java多线程 1
uwupu 啦啦啦啦啦

Java 多线程 1

程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;
进程:执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位;
一个进程中可以包含若干个线程,一个进程至少有一个线程。
线程是CPU调度和执行的单位。

线程就是独立的执行路径
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
main()可以称为主线程,为系统的入口,用于执行整个程序;

线程创建

.三种方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callback接口

继承Thread类

  1. 自定义类继承Thread类
  2. 重写**run()**方法,编写线程执行体;
  3. 创建线程对象,调用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();
}
}
//目标:You要Eat
//过程:代理Restaurant,Restaurant准备饭前饭后要做的事情
//结果:Restaurant代理You去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 表达式

函数式接口

  1. 任何接口,如果只包含一个抽象方法,则是一个函数式接口
  2. 对于函数式接口,可以通过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) {
//lambda表达式
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. 简化花括号: 若代码只有一行,这可以简化花括号

简化括号

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);
//当i==60,请求线程停止
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,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。

存在问题:

  1. 一个线程持有锁会导致其他所有需要锁的线程挂起;
  2. 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

线程同步安全 synchronized

synchronized有两种用法:synchronized方法和synchronized块。
同步方法public synchronized void method(int args){}

synchronized方法必须获得调用该方法的对象的锁才能执行,否则线程会阻塞。方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。

方法里面需要修改的内容需要锁。只读的代码不需要锁。

缺陷:若将一个大的方法申明为synchronized将会影响效率。

synchronized锁住的对象

  • 修饰实例方法:作用于当前实例加锁。
  • 修饰静态方法:作用于当前类对象加锁(锁住Class对象,然后,该对象的静态方法都得等待)。
  • 修饰代码块:指定加锁对象,对给定对象加锁。

加锁之后,锁只对有synchronized修饰的方法有效。

同步块

同步块:synchronized(Obj){}

Obj称为同步监视器。
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器。
Obj一般是同步块内变化的量。(增删改的对象)

CopyOnWriteArrayList

安全的ArrayList

死锁

产生死锁必要条件

  1. 互斥条件:一个资源每次只能被一个进程使用;
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
  3. 不剥夺条件:进程进程已获得的资源,在未使用完之前,不能强行剥夺;
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

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();
}
}
}
}
 评论