public String getName() :获取当前线程名称。
public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run() :此线程要执行的任务在此处定义代码。
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread() :返回对当前正在执行的线程对象的引用
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出
public static void yield():暂停当前正在执行的线程对象,并执行其他线程
public void interrupt():方法只是改变中断状态而已,它不会中断一个正在运行的线程
public final void join(): 等待该线程终止,Join这行代码在哪个线程上运行,就是哪个线程在等待,等待的是使用join方法的这个线程对象执行完。
实际上,join方法使得等待的线程阻塞,阻塞的是join这行代码在执行的那个线程上的线程。
//1.获取线程名称public class Demo { public static void main(String[] args) { // 获取main线程名 Thread thread = Thread.currentThread(); System.out.println(thread.getName()); // 创建对象 MyThread t1 = new MyThread(); //Thread(String name) 利用构造函数给线程设置名字 // 分配新的 Thread 对象。 // 给线程设置名字 t1.setName("吴彦祖"); // 通过getName方法得到的默认线程名 Thread-0 String name = t1.getName(); MyThread t2 = new MyThread("彭于晏"); String name1 = t2.getName(); System.out.println(name1); //System.out.println(name1); //System.out.println(name); //启动线程 t1.start(); //t2.start(); }}// 继承Threadclass MyThread extends Thread { public MyThread(String name) { super(name); } public MyThread() { } @Override public void run() { // 在run方法中也能获取到线程名 System.out.println(Thread.currentThread().getName() + ":hello thread"); }}//2.守护线程public class ThreadDaemonDemo { public static void main(String[] args) throws InterruptedException { // 创建对象 ThreadDaemon threadDaemon = new ThreadDaemon(); threadDaemon.setName("PDD"); // 标记成守护线程 threadDaemon.setDaemon(true); // 启动线程 threadDaemon.start(); // 休眠3s Thread.sleep(3000); //System.out.println("123"); }}class ThreadDaemon extends Thread { // 重写run方法 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + "-----" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }}//3.线程合并public class ThreadJoinDemo { public static void main(String[] args) throws InterruptedException { System.out.println("main start"); // 创建对象 ThreadJoin threadJoin = new ThreadJoin(); threadJoin.setName("正方形打野"); // 开启线程 threadJoin.start(); threadJoin.join(); System.out.println("main end"); }}class ThreadJoin extends Thread { // 重写run方法 @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName() + "------" + i); } }}//4.线程休眠public class ThreadSleepDemo { public static void main(String[] args) { // 创建子类对象 ThreadSleep t = new ThreadSleep(); // 启动线程 t.start(); }}class ThreadSleep extends Thread { @Override public void run() { System.out.println("sleep before"); try { // 当休眠时间到,就会自己醒来。 Thread.sleep(2000); // TimeUnit.MILLISECONDS.sleep() 可以按毫秒,秒,分钟,小时,天 // TimeUnit.SECONDS.sleep(2); // TimeUnit.HOURS.sleep(1); // TimeUnit.DAYS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sleep after"); }}//5.线程终止public class ThreadStop { public static void main(String[] args) throws InterruptedException { // 创建对象 ThreadInterrupt threadInterrupt = new ThreadInterrupt(); // 启动线程 threadInterrupt.start(); Thread.sleep(3000); // 泼了一盆冷水,叫醒 // 实际上是根据java当中的异常处理机制 //threadInterrupt.interrupt(); threadInterrupt.stop(); }}class ThreadInterrupt extends Thread { // 重写run方法 @Override public void run() { System.out.println("run start"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } // stop方法具有固有的不安全性 // 这里就不会再执行了 System.out.println("run end"); }}//6.线程礼让public class ThreadYieldDemo { public static void main(String[] args) { // 创建对象 ThreadYield threadYield = new ThreadYield(); ThreadYield threadYield2 = new ThreadYield(); threadYield.setName("55开"); threadYield2.setName("卢本伟"); // 启动2个线程 threadYield.start(); threadYield2.start(); }}class ThreadYield extends Thread { // 重写run方法 @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName()+"------"+i); yield(); // 暂停当前正在执行的线程对象, // 并执行其他线程。 // 虽然放弃了CPU的执行权,但是我们java当中采用的是抢占式的调度方式 // 所以下一次不一定是谁抢到 } }}
public class MyThread extends Thread{ /* * 利用继承中的特点 * 将线程名称传递 进行设置 */ public MyThread(String name){ super(name); } /* * 重写run方法 * 定义线程要执行的代码 */ public void run(){ for (int i = 0; i < 20; i++) { //getName()方法 来自父亲 System.out.println(getName()+i); } }}public class Demo { public static void main(String[] args) { System.out.println("这里是main线程"); MyThread mt = new MyThread("小强"); mt.start();//开启了一个新的线程 for (int i = 0; i < 20; i++) { System.out.println("旺财:"+i); } }}//对于这种方式实现的多线程,如果存在变量不一致的问题,可以采用static关键字来解决。
public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } }}public class Demo { public static void main(String[] args) { //创建自定义类对象 线程任务对象 MyRunnable mr = new MyRunnable(); //创建线程对象 Thread t = new Thread(mr, "小强"); t.start(); for (int i = 0; i < 20; i++) { System.out.println("旺财 " + i); } }}
- Thread类实际上也是实现了Runnable接口的类。
- 在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
- 实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。
public class NoNameInnerClassThread { public static void main(String[] args) { //new Runnable(){ // public void run(){ // for (int i = 0; i < 20; i++) { // System.out.println("张宇:"+i); // } // } //}; //‐‐‐这个整体 相当于new MyRunnable() Runnable r = new Runnable(){ public void run(){ for (int i = 0; i < 20; i++) { System.out.println("张宇:"+i); } } }; new Thread(r).start(); for (int i = 0; i < 20; i++) { System.out.println("费玉清:"+i); } }}
注:在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用Java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程
synchronized(同步锁){ //需要同步操作的代码}public class Ticket implements Runnable{ private int ticket = 100; Object lock = new Object(); /* * 执行卖票操作 */ @Override public void run() { //每个窗口卖票的操作 //窗口 永远开启 while(true){ synchronized (lock) { if(ticket>0){//有票 可以卖 //出票操作 //使用sleep模拟一下出票时间 try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } //获取当前线程对象的名字 String name = Thread.currentThread().getName(); System.out.println(name+"正在卖:"+ticket‐‐); } } } }}public class Demo { public static void main(String[] args) { //创建线程任务对象 Ticket ticket = new Ticket(); //创建三个窗口对象 Thread t1 = new Thread(ticket, "窗口1"); Thread t2 = new Thread(ticket, "窗口2"); Thread t3 = new Thread(ticket, "窗口3"); //同时卖票 t1.start(); t2.start(); t3.start(); }}
同步锁:
- 锁对象可以是任意的Java对象。
- 对于非static方法,同步锁对象就是this。
- 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
public synchronized void method(){ //可能会产生线程安全问题的代码}public class Ticket implements Runnable{ private int ticket = 100; /* * 执行卖票操作 */ @Override public void run() { //每个窗口卖票的操作 //窗口 永远开启 while(true){ sellTicket(); } } /* * 锁对象:是谁调用这个方法,就是谁 * 隐含锁对象就是 this */ public synchronized void sellTicket(){ if(ticket>0){//有票 可以卖 //出票操作 //使用sleep模拟一下出票时间 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } //获取当前线程对象的名字 String name = Thread.currentThread().getName(); System.out.println(name+"正在卖:"+ticket‐‐); } }}
public class Ticket implements Runnable{ private int ticket = 100; Lock lock = new ReentrantLock(); /* * 执行卖票操作 */ @Override public void run() { //每个窗口卖票的操作 //窗口 永远开启 while(true){ lock.lock(); if(ticket>0){//有票 可以卖 //出票操作 //使用sleep模拟一下出票时间 try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto‐generated catch block e.printStackTrace(); } //获取当前线程对象的名字 String name = Thread.currentThread().getName(); System.out.println(name+"正在卖:"+ticket--); } lock.unlock(); } }}
线程状态 | 导致状态发生条件 |
---|---|
New(新建) | 线程刚被创建,但是并未启动。还没调用start方法。 |
Runnable(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。 |
TimedWaiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。 |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |
public class MyThread extends Thread { public void run() { for (int i = 0; i < 100; i++) { if ((i) % 10 == 0) { System.out.println("‐‐‐‐‐‐‐" + i); } System.out.print(i); try { Thread.sleep(1000); System.out.print(" 线程睡眠1秒!\n"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { new MyThread().start(); } }
public class WaitingTest { public static Object obj = new Object(); public static void main(String[] args) { // 演示waiting new Thread(new Runnable() { @Override public void run() { while (true){ synchronized (obj){ try { System.out.println( Thread.currentThread().getName() +"=== 象,调用wait方法,进入waiting状态,释放锁对象"); obj.wait(); //无限等待 //obj.wait(5000); //计时等待, 5秒 时间到,自动醒来 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( Thread.currentThread().getName() + "=== 从waiting状态醒来,获取到锁对象,继续执行了"); } } } },"等待线程").start(); new Thread(new Runnable() { @Override public void run() { // while (true){ //每隔3秒 唤醒一次 try { System.out.println( Thread.currentThread().getName() +"‐‐‐‐‐ 等待3秒钟"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj){ System.out.println( Thread.currentThread().getName() +"‐‐‐‐‐ 获取到锁对象,调用notify方法,释放锁对象"); obj.notify(); } } // } },"唤醒线程").start(); } }
注:
我们在翻阅API的时候会发现Timed Waiting(计时等待) 与 Waiting(无限等待) 状态联系还是很紧密的,比如Waiting(无限等待) 状态中wait方法是空参的,而timed waiting(计时等待) 中wait方法是带参的。这种带参的方法,其实是一种倒计时操作,相当于我们生活中的小闹钟,我们设定好时间,到时通知,可是如果提前得到(唤醒)通知,那么设定好时间在通知也就显得多此一举了,那么这种设计方案其实是一举两得。如果没有得到(唤醒)通知,那么线程就处于Timed Waiting状态,直到倒计时完毕自动醒来;如果在倒计时期间得到(唤醒)通知,那么线程从Timed Waiting状态立刻唤醒。
个人理解:死锁如果用通俗易懂的话来解释,其实就像我们生活中,和迎面而来的人相遇,给对面的人让路,对面的人也给我们让路,然而两个人让路的方向是同一边,这样就是死锁。
public class Demo { public static void main(String[] args) { DieLock dieLock1 = new DieLock(true); DieLock dieLock2 = new DieLock(false); new Thread(dieLock1).start(); new Thread(dieLock2).start(); }}// 描述锁对象class MyLock { public static final Object objA = new Object(); public static final Object objB = new Object(); public static final Object objAB = new Object();}class DieLock implements Runnable { boolean flag; public DieLock(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { synchronized (MyLock.objA) { System.out.println("if A"); // 发生了线程切换 synchronized (MyLock.objB) { System.out.println("if B"); } } } } else { synchronized (MyLock.objB) { System.out.println("else B"); // 打印了else B synchronized (MyLock.objA) { System.out.println("else A"); } } } //解决方法: //1.新加一把锁 if (flag) { synchronized (MyLock.objAB) { synchronized (MyLock.objA) { System.out.println("if A"); // 发生了线程切换 synchronized (MyLock.objB) { System.out.println("if B"); } } }else { synchronized (MyLock.objAB) { synchronized (MyLock.objB) { System.out.println("else B"); // 打印了else B synchronized (MyLock.objA) { System.out.println("else A"); } } } } //2.更改加锁顺序,就是把原来程序的其中一个分支调换顺序 }}
public class Box { // 包子 Food food; // 生产包子 只有生产者线程调用setFood方法 public synchronized void setFood(Food newFood) throws InterruptedException { //先判断蒸笼是否为空 //如果蒸笼为空,没有包子 if (food == null) { //做包子并放入蒸笼, food = newFood; System.out.println(Thread.currentThread().getName() + "做了:" + food); Thread.sleep(3000); // 通知消费者来吃 notify(); //notifyAll(); } else { //如果蒸笼非空 //说明蒸笼里有包子, // 阻止自己,不在生产 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } //System.out.println("生产者 wait 后面的代码"); } } // 吃包子 只有消费者线程调用eatFood方法 public synchronized void eatFood() throws InterruptedException { // 先判断蒸笼状态 if (food == null) { //如果蒸笼为空,没有包子 //阻止自己 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } //System.out.println("消费者 wait 后面的代码"); } else { //如果蒸笼非空 有包子 //吃包子 System.out.println(Thread.currentThread().getName() + "吃了:" + food); Thread.sleep(3000); food = null; //通知生产者去再生产包子 notify(); //notifyAll(); } }}// 该类描述包子class Food { String name; int price; public Food(String name, int price) { this.name = name; this.price = price; } @Override public String toString() { return "Food{" + "name='" + name + '\'' + ", price=" + price + '}'; }}public class ConsumerTask implements Runnable { // 蒸笼 Box box; public ConsumerTask(Box box) { this.box = box; } @Override public void run() { while (true) { try { box.eatFood(); } catch (InterruptedException e) { e.printStackTrace(); } } }}public class ProducerTask implements Runnable { Box box; Food[] foods = {new Food("庆丰包子", 8), new Food("上海生煎包", 2), new Food("广式叉烧包", 10)}; Random random; public ProducerTask(Box box) { this.box = box; random = new Random(); } @Override public void run() { while (true) { // 只生产包子 int index = random.nextInt(foods.length); try { box.setFood(foods[index]); } catch (InterruptedException e) { e.printStackTrace(); } } }}public class Demo { public static void main(String[] args) throws InterruptedException { Box box = new Box(); ProducerTask producerTask = new ProducerTask(box); ConsumerTask consumerTask = new ConsumerTask(box); //创建2个线程并启动 // 生产者线程 Thread t1 = new Thread(producerTask); // 消费者线程 Thread t2 = new Thread(consumerTask); Thread t3 = new Thread(producerTask); // 消费者线程 Thread t4 = new Thread(consumerTask); t1.setName("生产者1"); t2.setName("消费者1"); t3.setName("生产者2"); t4.setName("消费者2"); t1.start(); t2.start(); t3.start(); t4.start(); }}
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUEExecutors.newSingleThreadExecutor(); //创建容量为1的缓冲池Executors.newFixedThreadPool(int n); //创建固定容量大小为n的缓冲池
public class ThreadPoolDemo { public static void main(String[] args) { // 创建线程池对象 ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象 // 创建Runnable实例对象 MyRunnable r = new MyRunnable(); //自己创建线程对象的方式 // Thread t = new Thread(r); // t.start(); ‐‐‐> 调用MyRunnable中的run() // 从线程池中获取线程对象,然后调用MyRunnable中的run() service.submit(r); // 再获取个线程对象,调用MyRunnable中的run() service.submit(r); //线程池只有两个线程对象,此任务会进入队列,等待下一轮执行 service.submit(r); // 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。 // 将使用完的线程又归还到了线程池中 // 关闭线程池,执行完全部任务后关闭 //service.shutdown(); //执行两个任务就会关闭 //service.shutdownNow(); }}class MyRunnable implements Runnable { @Override public void run() { System.out.println("我要一个教练"); try { //这里的sleep函数可以清楚地看到执行顺序 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("教练来了: " + Thread.currentThread().getName()); System.out.println("教我游泳,教完后,教练回到了游泳池"); }}
//创建定时器Timer timer = new Timer()//调度定时任务//在指定的时间点,调度定时任务执行schedule(TimerTask task, Date time)//在delay毫秒的延时之后,首次调度task执行,之后每period毫秒执行一次定时任务schedule(TimerTask task, long delay, long period)//在firstTime时间点,首次执行,之后每period毫秒执行一次schedule(TimerTask task, Date firstTime, long period)//在delay毫秒的延时后,首次调度task执行,之后每period毫秒执行一次scheduleAtFixedRate(TimerTask task, long delay, long period) SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date firstTime = simpleDateFormat.parse("2021-01-23 07:38:59");
public class Demo { public static void main(String[] args) throws ParseException { // /在指定的时间点,调度定时任务执行 //schedule(TimerTask task, Date time) 在delay毫秒的延时之后,首次调度task执行,之后每period毫秒执行一次定时任务 //schedule(TimerTask task, long delay, long period) // 创建定时器 Timer timer = new Timer(); // 进行任务调度 String s = "2021-01-23 11:43:40"; // SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //Date firstTime = simpleDateFormat.parse("2021-01-23 07:38:59"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = simpleDateFormat.parse(s); //timer.schedule(new MyTask(),date); timer.schedule(new MyTask(),3000,5000); timer.cancel(); }}/***如何定义一个定时任务**- 继承TimerTask- 重写run方法* */class MyTask extends TimerTask { @Override public void run() { // 定时任务要做的事情写到run方法里 System.out.println("boom! 爆炸了!"); }}
public class Demo { public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建线程池 ExecutorService pool = Executors.newFixedThreadPool(2); // 提交任务 Future future = pool.submit(new MyCallable()); //get() 获取计算结果 //如有必要,等待计算完成,然后获取其结果。 System.out.println("get before"); String s = (String) future.get(); System.out.println(s); }}/*可以理解为多线程的实现方式3 ,但是必须要依赖线程池*/class MyCallable implements Callable { @Override public Object call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "------" +i); } Thread.sleep(3000); return "hello"; }}
关于volatile原子性可以理解为把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步
第一个操作/第二个操作 | 普通读写 | volatile读 | volatile写 |
---|---|---|---|
普通读写 | 不允许 | ||
volatile读 | 不允许 | 不允许 | 不允许 |
volatile写 | 不允许 | 不允许 |