多线程
多行并发
- 需求:一边打LOL,一边听网易云音乐
- 问题:程序间断的问题
- 多进程或者多线程来解决
- 并行:多件事情在同一时间点发生
- 并发:多件事情在同一时间段发生,同一时间段运行多个程序
- 单个cpu:一个时间点只能执行一个程序,可以交替执行
线程与进程
- 进程:内存中的应用程序,每个进程都是一块独立的内存空间
- 线程:进程中执行任务的单元,一个进程可以同时并发的运行多个线程
- 进程:进程有独立的内存空间,一个进程中至少有一个线程
- 线程:栈空间是独立的,堆空间是共享的
- 多线程的优势:因为堆空间是共享的,所以对内存的开销比较小
- 多线程取决于cpu调度
- java程序最少包含两个线程(主线程,GC垃圾回收线程)
- 线程的调度:jvm采用抢占式调用,没有采用分时调用,执行的结果是随机的
线程的实现方式
-
继承Thread类
-
重写run方法
-
启动线程,调用start
//创建类,继承Thread public class MusicDemo extends Thread{ @Override//重写run方法 public void run() { for (int i = 0; i < 100; i++) { System.out.println("播放音乐:"+i); } } } //启动线程 MusicDemo musicDemo = new MusicDemo(); musicDemo.start();
-
**注意:**线程的启动不是去手动调用run方法,需要调用线程的start方法
-
实现Runnable接口
-
实现Runnable 接口,实现run方法
-
启动:
-
实现impl = new 实现类();
-
Thread t = new Thread (impl);
-
t.start();
public class MusicDemo implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("播放音乐:"+i); } } } public class RunnableDemo { public static void main(String[] args) { MusicDemo musicDemo = new MusicDemo(); Thread thread = new Thread(musicDemo); thread.start(); for (int i = 0; i < 100; i++) { System.out.println("打游戏:"+i); } } }
-
-
-
使用匿名内部类的方式创建线程(某个线程只需要使用一次)
线程调度
-
线程的控制操作:
-
线程的合并
-
join 方法
MusicDemo musicDemo = new MusicDemo(); musicDemo.start(); for (int i = 0; i <20; i++) { System.out.println("打游戏:"+i); if(i==6){ musicDemo.join(); } }
-
-
守护线程:GC使用的
-
目的:为其他线程服务的(GC典型的守护线程)
-
主线程结束,守护线程也结束
-
创建线程默认是前台线程(主线程)
-
获取守护线程 : thread.setDaemon(true);
daemonThread.setDaemon(true);//守护线程,必须设置在start();方法前面,主线程结束,它也结束 daemonThread.start();
-
-
获取当前线程的名称
- Thread.currentThread().getName();
-
线程设置优先级
-
优先级的高低并不会完全影响执行的先后顺序,只是优先级高,执行的机会大
public final static int MAX_PRIORITY = 10; public final static int NORM_PRIORITY = 5; public final static int MIN_PRIORITY = 1;
-
-
线程让位
-
yield 方法:当前线程自愿让出cpu资源
-
yield 和 sleep区别:
- 都能使当前运行线程放弃cpu运行机会,把机会让给其他线程
- sleep给线程机会的时候,不考虑优先级
- yield 只会给相同优先级或者更高优先级机会
public class Thread1 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("线程1:"+i); } Thread.yield();//让出线程 } }
-
-
线程同步
-
需求:猴子吃香蕉(30根香蕉,3个猴子)
-
使用继承 Thread 方式
- 同一个编号的香蕉被多个猴子吃
-
使用实现Runnable接口
private int num = 30; @Override public void run() { for (int i = 0; i < 30; i++) { if (num > 0) { System.out.println(Thread.currentThread().getName() + "吃了编号为:" + num-- + "的香蕉。"); } } } public static void main(String[] args) { Banana banana = new Banana(); Thread thread1 = new Thread(banana,"AAA猴子"); thread1.start(); Thread thread2 = new Thread(banana,"BBB猴子"); thread2.start(); Thread thread3 = new Thread(banana,"CCC猴子"); thread3.start(); }
-
-
-
解决方案:多个猴子吃到同一个香蕉
-
同步代码块
-
语法
synchronized(同步锁){ 业务逻辑 }
-
-
同步方法
-
语法
synchronized pubilc void eat(){ //业务逻辑 }
-
-
锁机制(Lock)
- 语法
//创建锁 private final Lock lock = new ReentrantLock(); //上锁 lock.lock(); //释放锁 lock.unlock();同步代码块,同步方法,同步锁的选用?
-
-
同步代码块,同步方法,同步锁如何选用?
-
尽量减少锁的作用域(synchronized锁方法)
-
建议使用同步锁Lock,可以很好地控制锁的作用域
-