目录
线程创建
- 自定义线程类继承自Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
/** * 创建线程 */ public class test_thread extends Thread { @Override public void run() { // run() 方法线程体 for (int i = 0; i < 200; i++) { System.out.println("我在子线程"+i); } } public static void main(String[] args){ // 创建一个线程对象 test_thread test_thread1 = new test_thread(); // 调用start方法开启线程 test_thread1.start(); // main线程,主线程 for (int i = 0; i < 200; i++) { System.out.println("我在主线程"+i); } } }
- 【注】若直接调用子线程的run()方法,则不是多线程交替执行,而会调用run()方法执行
- 【注】线程开启,不一定立即执行,由cpu调度执行。
实现Runnable
- 定义类实现Runnable接口
- 重写run()方法,编写线程执行体
- 创建线程thread对象,将实现接口放入,调用start()方法启动线程
package thread; public class MyRunnable implements Runnable { @Override public void run() { // run() 方法线程体 for (int i = 0; i < 200; i++) { System.out.println("我在子线程"+i); } } public static void main(String[] args){ // 创建runnable 接口的实现类对象 MyRunnable myRunnable = new MyRunnable(); // 创建线程对象,丢入实现类 Thread thread = new Thread(myRunnable); // 开启线程 thread.start(); // main线程,主线程 for (int i = 0; i < 200; i++) { System.out.println("我在主线程"+i); } } }
对比
- 继承Thread类
- 子类继承Thread类具备多线程能力
- 启动,子类对象调用strat方法
- 不建议用,避免OOP单继承局限性
- 实现Runnable接口
- 实现Runnable接口具有多线程能力
- 启动,传入目标对象+thread对象.start()
- 推荐用,避免单继承局限性。方便一个对象同时被多个线程使用
初识java并发问题
- 当多个线程同时操作一个共享资源时,会发生数据紊乱问题。
Lamda表达式
- 首先介绍函数式接口、
- 任何接口,如果只包含唯一一个抽象方法,那它就是一个函数式接口(例如Runnable接口,包含一个run的抽象方法)
- 对于函数式接口,可以和Lamda表达式来创建该接口的对象
- 推导lamda表达式,在没有lamda表达式时,我们是这样做的
package lamda; /** * 推导Lamda表达式 * */ public class TestLamda { public static void main(String[] args) { Like ilike = new Like(); ilike.lamda(); } } // 1.定义一个函数式接口 interface Ilike{ void lamda(); } // 2.实现类 class Like implements Ilike{ @Override public void lamda() { System.out.println("i like lamda"); } }
- 然后,我们进行修改,使用静态内部类
//3. 静态内部类
static class Like2 implements Ilike{
@Override
public void lamda() {
System.out.println("i like lamda");
}
}
public static void main(String[] args) {
Like ilike = new Like();
ilike.lamda();
Like2 like2 = new Like2();
like2.lamda();
}
- 再进一步简化,局部内部类
//4. 局部内部类
class Like3 implements Ilike{
@Override
public void lamda() {
System.out.println("i like lamda3");
}
}
like = new Like3();
like.lamda();
- 再进一步简化,去类名,匿名内部类
//5 匿名内部类,没有类的名称,借助接口或者父类
like = new Ilike() {
@Override
public void lamda() {
System.out.println("i like lamda5");
}
};
like.lamda();
- 再简化,lamda(什么都不要,只保留了方法)
//6,用lamda简化
like = ()->{
System.out.println("i like lamda6");};
like.lamda();
- 【注】lamda表达式必须是函数式接口
- 完整代码(较上有改动)
package lamda;
/**
* 推导Lamda表达式
*
*/
public class TestLamda {
//3. 静态内部类
static class Like2 implements Ilike{
@Override
public void lamda() {
System.out.println("i like lamda2");
}
}
public static void main(String[] args) {
Ilike like = new Like();
like.lamda();
like = new Like2();
like.lamda();
//4. 局部内部类
class Like3 implements Ilike{
@Override
public void lamda() {
System.out.println("i like lamda3");
}
}
like = new Like3();
like.lamda();
//5 匿名内部类
like = new Ilike() {
@Override
public void lamda() {
System.out.println("i like lamda5");
}
};
like.lamda();
//6,用lamda简化
like = ()->{
System.out.println("i like lamda6");};
like.lamda();
}
}
// 1.定义一个函数式接口
interface Ilike{
void lamda();
}
// 2.实现类
class Like implements Ilike{
@Override
public void lamda() {
System.out.println("i like lamda");
}
}
- 完整的一个lamda表达式
Ilike like1;
like1 =()->{
System.out.println("i like lamda6");};
like1.lamda();
- 带参数的lamda示例
package lamda;
public class test_lamda {
public static void main(String[] args) {
Ilove ilove;
// ilove = new Love();
// ilove.love(2);
ilove= (int a)->{
System.out.println("i love you 2,"+a);
};
ilove.love(4);
}
}
interface Ilove{
void love(int aa);
}
//class Love implements Ilove{
//
// @Override
// public void love(int a) {
// System.out.println("i love you "+a);
// }
//}
线程状态
- 创建状态:Thread t = new Thread()
- 就绪状态: 调用start() 方法,进入就绪状态,不一定立即执行
- 运行 :经cpu调度分配资源后
- 阻塞 :调用sleep,wait,同步锁定,进入阻塞
- 销毁 :中断,结束
- java源码中,以枚举的方式列举了6种状态
下文这段为对原文的翻译,来自下方博客链接public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called {@code Object.wait()} * on an object is waiting for another thread to call * {@code Object.notify()} or {@code Object.notifyAll()} on * that object. A thread that has called {@code Thread.join()} * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
https://blog.youkuaiyun.com/weixin_43740223/article/details/120338375?utm_medium=distribute.pc_feed_v2.none-task-blog-personrec_tag-13.pc_personrecdepth_1-utm_source=distribute.pc_feed_v2.none-task-blog-personrec_tag-13.pc_personrec
```java
public enum State {
/**
* 尚未启动的线程的线程状态。线程对象已经被创建了,但是还没有启动的状态
*/
NEW,
/**
* 可运行线程的线程状态。表示线程可能正在运行或者在等待CPU资源。
*/
RUNNABLE,
/**
* 等待java monitor对象锁资源而阻塞的线程状态。
* 1.处于阻塞状态的线程正在等待监视器锁进入synchorinzed代码块/方法
* 2.在调用Object.wait释放了monitor锁资源后重新进入synchorinzed代码块/方法。
*/
BLOCKED,
/**
* 1.等待线程的线程状态。由于调用以下方法之一,线程处于等待状态:
* Object.wait没有超时
* Thread.join没有超时
* LockSupport.park
* 2.处于等待状态的线程正在等待另一个线程执行特定操作。例如,
* 在对象上调用Object.wait()的线程正在等待另一个线程在该对象上调用Object.notify()或Object.notifyAll()。
* 调用Thread.join() 的线程正在等待指定的线程终止。
*/
WAITING,
/**
* 具有指定等待时间的等待线程的线程状态。 由于使用指定的正等待时间调用以下方法之一,线程处于定时等待状态:
* - Thread.sleep(millis)
* - Object.wait(timeout)
* - Thread.join(millis)
* - LockSupport.parkNanos(blocker, nanos)
* - LockSupport.parkUntil(blocker, deadline)
*/
TIMED_WAITING,
/**
* 终止线程的线程状态。 线程已完成执行。
*/
TERMINATED;
}
```
方法
- setPriority(int newPriority)更改线程的优先级
- static void sleep(long millis)指定毫秒数内休眠
- join() 等待线程终止
- yield 暂停正在执行,执行其他线程
- interrupt() 中断线程,不用此方式
- isAlive() 测试线程是否处于活动状态
- 停止线程:不推荐系统的方法
- 推荐自己停止下来
- 建议使用个标志位,当flag=false,线程终止运行
package lamda; public class test_stop implements Runnable { private boolean flag = true; @Override public void run() { //线程体使用标识 while (flag){ System.out.println("run...thread"); } } // 对外提供方法改变标识 public void stop(){ flag = false; } }
线程休眠
- sleep时间达到后进入就绪状态
- 每个对象都有一个锁,sleep不会释放锁
- 利用sleep一直获取系统当前时间
- 可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
package state; import javafx.scene.chart.PieChart; import java.text.SimpleDateFormat; import java.util.Date; /** * 打印系统当前时间 */ public class TestSleep { public static void main(String[] args) throws InterruptedException { Date starttime = new Date(System.currentTimeMillis()); while (true){ System.out.println(new SimpleDateFormat("hh:mm:ss").format(starttime)); Thread.sleep(1000); starttime = new Date(System.currentTimeMillis()); } } }
线程礼让
- 礼让线程,只是暂停正在执行的当前线程,但是不阻塞
- 线程从运行状态到就绪状态
- 让cpu重新调度,礼让不一定成功,只是自己放弃,出去重新竞争(还是由cpu自己选择)
- 实例
package state;
// 测试礼让
/**
* 礼让不一定成功,看cpu心情
*/
public class Testyield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
礼让成功结果:
- 测了半天全都礼让成功了,不成功的状态应该是一个线程全部执行完 ,再执行另一个线程。
强制执行Join
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
- 可以想象成插队(强制执行,霸道儿)
package state;
/**
* 测试Joinf方法
*
*/
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
// 主线程
for (int i = 0; i < 500; i++) {
if (i == 20){
thread.join();
}
System.out.println("主线程来了"+i);
}
}
}
线程状态观测
package state;
/**
* 观察测试线程的状态
*
*/
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("/");
});
// 观察状态
Thread.State state = thread.getState();
System.out.println(state);
//更新启动后
thread.start();
state = thread.getState();
System.out.println(state);
while (state !=Thread.State.TERMINATED){//只要线程不中止,就一直输出线程的状态
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
Runnable 运行状态中
-【注】当线程死亡后,不可以再启动,会报错,线程不可以启动两次
为什么启动线程是调用start()方法,而不是调用run方法呢?
- 这是因为在new了一个线程后,首先进入初始态,调用start方法来到就绪态,并不会立即执行,而会等待系统资源分配,分到时间片就可以运行了。
start是一个native方法,将启动一个新线程,并执行run方法,而直接执行run,会当作main线程下的一个类内方法,不会在某个线程执行,所以不行
为什么只能调用一次start方法
- 首先,当线程死亡后,再调用start方法会报IllegalThreadStateException,这是因为在start方法的源码中,首先会判断线程的状态,如果线程不是初始状态,就会抛出异常,所以,再次执行就会报错。
线程优先级
- java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定哪个线程优先执行
- 线程优先级用数字表示 范围(1—10)
- 使用以下方式改变或获取优先级
- getPriority
- setPriority(int xxxx)
package state;
/**
* 测试线程的优先级
*/
public class TestPriority {
public static void main(String[] args) {
// 主线程默认优先级
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread thread1 = new Thread(myPriority,"t1");
Thread thread2 = new Thread(myPriority,"t2");
Thread thread3 = new Thread(myPriority,"t3");
Thread thread4 = new Thread(myPriority,"t4");
Thread thread5 = new Thread(myPriority,"t5");
// 先设置优先级,再启动
thread1.start();
thread2.setPriority(2);
thread2.start();
thread3.setPriority(4);
thread3.start();
thread4.setPriority(6);
thread4.start();
thread5.setPriority(10);
thread5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
}
}
- 【注】主线程默认优先级5,不能改
- 【注】优先级 范围 1-10
- 【注】优先级低只是意味着获得调度的概率低,并不是一定严格按照优先级大小进行调度,(还是全看cpu心情)
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕(例如 main线程)
- 虚拟机不用等待守护线程执行完毕(例如 gc线程)
- 实例:当用户线程执行完毕后,虚拟机不需要等待守护线程执行完毕,下例,当用户线程执行完毕后,守护线程过一段时间就结束了。
package state;
/**
* 测试守护线程
* 案例 上帝守护你
*/
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread t1 = new Thread(god);
t1.setDaemon(true);// 默认是false 表示是用户线程,true是守护线程
t1.start(); // 守护线程启动
new Thread(you).start();
}
}
// 上帝
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝保佑着你");
}
}
}
// 你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一生开心的活着");
}
System.out.println("good bye");
}
}
线程同步机制
问题的发生
- 多个线程操作同一个资源,就是并发问题,这个时候就需要我们线程同步,本质是一种等待机制,想访问此对象的多个线程进入这个对象的等待池,形成队列,等待前面线程使用完毕,下一个线程再使用。
- 为了保证数据访问正确性,加入了锁机制,synchronized,线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可,但是会存在以下问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁,释放锁,会导致较多上下文切换和调度延时,引起性能问题
- 若低优先级进程获得锁,而高优先级进程等待释放锁,会导致优先级倒置,引起性能问题
- 三大线程不安全案例,syn文件夹下,此处就不贴了
- 买票
- 银行取钱
- 不安全集合,Arraylist
因为list集合可能同时操作了一个位置,造成了覆盖,所以数据缺失。package syn; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** * 线程不安全的集合 */ public class Unsafelist { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{list.add(Thread.currentThread().getName());}).start(); } // T Thread.sleep(3000); System.out.println(list.size()); } }
解决方法(同步方法、同步块)
-
synchronized 关键字
- synchronized控制对“对象的访问”,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到方法返回才释放该锁,后面被阻塞的线程才能获得这个锁,继续执行
- 缺陷,将一个大的方法声明为sybchronized会影响效率
- 方法里面修改的内容才需要锁,锁的太多,浪费资源
- 同步块 :synchronized(obj){} 称之为同步监视器
- synchronized:默认锁的是对象类本身(this)
- 同步块可以锁任何对象
- 锁的对象一定是变化的量,需要增删改查的量
-
同步块
- synchronized(obj){}
- Obj 称之为同步监视器
- Obj可以是任何对象,但是推荐使用任何资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是对象本身,或者是class【反射中讲解】
- 同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问完毕,发现同步监视器没有锁,然后锁定并访问
生产者消费者问题
- 两者共享同一个资源,且两个线程需要相互通信(生产者有了通知消费者)
- 以上分析,仅有synchronized 是不够的
- synchronized 可阻止并发更新同一个共享资源,实现了同步
- synchronized 但是不能进行线程之间的消息传递
-解决方法1:管程法