/**
* 实现线程的第一种方式:
* 编写一个类,直接继承java.lang.Thread,重写run方法。
*
* 怎么创建线程对象? new就行了。
* 怎么启动线程呢? 调用线程对象的start()方法。
*
* 注意:
* 亘古不变的道理:
* 方法体当中的代码永远都是自上而下的顺序依次逐行执行的。
*
* 以下程序的输出结果有这样的特点:
* 有先有后。
* 有多有少。
*/
public class ThreadTest01 {
public static void main(String[] args) {
// 这里是main方法,这里的代码属于主线程,在主栈中运行。
// 新建一个分支线程对象
MyThread t=new MyThread();
// 启动线程
//t.run(); // 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
// start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
// 这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
// 启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
// run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
t.start();
// 这里的代码还是运行在主线程中。
for(int i=0;i<1000;i++){
System.out.println("主线程--------------"+i);
}
}
}
class MyThread extends Thread{
public void run(){
// 编写程序,这段程序运行在分支线程中(分支栈)。
for(int i=0;i<1000;i++){
System.out.println("分支线程--------------"+i);
}
}
}
运行结果:
/**
* 实现线程的第二种方式,编写一个类实现java.lang.Runnable接口。
*/
public class ThreadTest02 {
public static void main(String[] args) {
// 创建一个可运行的对象
MyRunnable r=new MyRunnable();
// 将可运行的对象封装成一个线程对象
Thread t=new Thread(r);
//Thread t=new Thread(new MyRunnable());以上两行代码的合并
//启动线程
t.start();
for(int i=0;i<100;i++){
System.out.println("主线程----------"+i);
}
}
}
// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("分支线程---------"+i);
}
}
}
代码结果:
/**
* 采用匿名内部类的方式
*/
public class ThreadTest03 {
public static void main(String[] args) {
Thread t=new Thread(new Runnable(){
public void run(){
for(int i=0;i<100;i++){
System.out.println("分线程-------"+i);
}
}
});
t.start();
for(int i=0;i<100;i++){
System.out.println("主线程----------"+i);
}
}
}
/**
* 1、怎么获取当前线程对象?
* Thread t = Thread.currentThread();
* 返回值t就是当前线程。
*
* 2、获取线程对象的名字
* String name = 线程对象.getName();
*
* 3、修改线程对象的名字
* 线程对象.setName("线程名字");
*
* 4、当线程没有设置名字的时候,默认的名字有什么规律?(了解一下)
* Thread-0
* Thread-1
* Thread-2
* Thread-3
* .....
*/
public class ThreadTest04 {
public void dosome(){
// 这样就不行了
//this.getName();
//super.getName();
//这样可以
String name=Thread.currentThread().getName();
System.out.println(name);
}
public static void main(String[] args) {
ThreadTest04 tt=new ThreadTest04();
tt.dosome();
//currentThread就是当前线程对象
// 这个代码出现在main方法当中,所以当前线程就是主线程。
Thread currentThead=Thread.currentThread();
System.out.println(currentThead.getName());
// 创建线程对象
MyThread2 t=new MyThread2();
// 设置线程的名字
t.setName("t1线程");
// 当没有设置线程的名字默认名字形式:Thread-0开始
//String tname=t.getName();
//System.out.println(tname);
MyThread2 t1=new MyThread2();
t1.setName("t2线程");
//System.out.println(t1.getName());
t.start();
t1.start();
}
}
class MyThread2 extends Thread{
public void run(){
for(int i=0;i<10;i++){
// currentThread就是当前线程对象。当前线程是谁呢?
// 当t1线程执行run方法,那么这个当前线程就是t1
// 当t2线程执行run方法,那么这个当前线程就是t2
Thread currentThread=Thread.currentThread();
System.out.println(currentThread.getName()+"--------"+i);
//System.out.println(this.getName()+"=========="+i);
//System.out.println(super.getName()+"---------"+i);
}
}
}
执行结果:
/**
* 关于线程的sleep方法:
* static void sleep(long millis)
* 1、静态方法:Thread.sleep(1000);
* 2、参数是毫秒
* 3、作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。
* 这行代码出现在A线程中,A线程就会进入休眠。
* 这行代码出现在B线程中,B线程就会进入休眠。
* 4、Thread.sleep()方法,可以做到这种效果:
* 间隔特定的时间,去执行一段特定的代码,每隔多久执行一次。
*/
public class ThreadTest05 {
public static void main(String[] args) {
try {
// 让当前线程进入休眠,睡眠5秒
// 当前线程是主线程!!!
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 5秒之后执行这里的代码
System.out.println("5秒之后显示");
for(int i=0;i<10;i++){
System.out.println("------------"+i);
try {
// 每输出一次睡眠1秒在继续输出
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadTest06 {
public static void main(String[] args) {
Thread t = new MyThread3();
t.setName("t");
t.start();
/* 调用sleep方法
问题:下面这行代码会让线程t进入休眠状态吗?
在执行的时候还是会转换成:
这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠。
这样代码出现在main方法中,main线程睡眠,t线程并不会受影响。*/
try {
t.sleep(1000 * 5);
//和下面这行代码效果相同
//Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("这段代码什么时候执行");
}
}
class MyThread3 extends Thread{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"----"+i);
}
}
}
/**
* sleep睡眠太久了,如果希望半道上醒来,你应该怎么办?也就是说怎么叫醒一个正在睡眠的线程??
* 注意:这个不是终断线程的执行,是终止线程的睡眠。
*/
public class ThreadTest07 {
public static void main(String[] args){
Thread t=new Thread(new MyRunnable2());
t.setName("t线程");
t.start();
// 希望5秒之后,t线程醒来(5秒之后主线程手里的活儿干完了。)
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
t.interrupt();
}
}
class MyRunnable2 implements Runnable{
// 重点:run()当中的异常不能throws,只能try catch
// 因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。
public void run(){
System.out.println(Thread.currentThread().getName()+"---begin");
try {
Thread.sleep(1000*60*60);
} catch (InterruptedException e) {
e.printStackTrace();
}
//1小时之后才会执行这里
System.out.println(Thread.currentThread().getName()+"-----end");
}
}
/**
* 怎么合理的终止一个线程的执行。这种方式是很常用的。
*/
public class ThreadTest08 {
public static void main(String[] args) {
MyRunnable4 r=new MyRunnable4();
Thread t=new Thread(r);
t.setName("t线程");
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终止线程
// 你想要什么时候终止t的执行,那么你把标记修改为false,就结束了。
r.run=false;
}
}
class MyRunnable4 implements Runnable{
// 打一个布尔标记
boolean run=true;
public void run(){
for(int i=0;i<10;i++){
if(run){
System.out.println(Thread.currentThread().getName()+"---------"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("进程结束");
// return就结束了,你在结束之前还有什么没保存的。
// 在这里可以保存呀。
//save....
//终止当前线程
return;
}
}
}
}
/**
* 关于线程的优先级
*/
public class ThreadTest09 {
public static void main(String[] args) {
// 设置主线程的优先级为1
Thread.currentThread().setPriority(1);
System.out.println("最高优先级" + Thread.MAX_PRIORITY); //10
System.out.println("最低优先级" + Thread.MIN_PRIORITY); //1
System.out.println("默认优先级" + Thread.NORM_PRIORITY); //5
Thread currentThread = Thread.currentThread();
Thread t = new Thread(new MyRunnable5());
//设置t线程的优先级为10
t.setPriority(10);
t.setName("t线程");
t.start();
// 优先级较高的,只是抢到的CPU时间片相对多一些。
// 大概率方向更偏向于优先级比较高的。
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
class MyRunnable5 implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName()+"默认优先级"+Thread.currentThread().getPriority());
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"----"+i);
}
}
}
/**
* 让位,当前线程暂停,回到就绪状态,让给其它线程。
* 静态方法:Thread.yield();
*/
public class ThreadTest11 {
public static void main(String[] args) {
Thread t=new Thread(new MyRunnable6());
t.setName("t线程");
t.start();
for(int i=0;i<10000;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
class MyRunnable6 implements Runnable{
public void run(){
for(int i=0;i<10000;i++){
//每100个让位一次。
if(i%100==0){
// 当前线程暂停一下,让给主线程。
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
运行结果
public class ThreadTest12 {
public static void main(String[] args) {
System.out.println("main begin");
Thread t=new Thread(new MyRunnable7());
t.setName("t线程");
t.start();
try {
// t合并到当前线程中,当前线程受阻塞,t线程执行直到结束。
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main end");
}
}
class MyRunnable7 implements Runnable{
public void run(){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
运行结果:
public class ThreadTest13 {
public static void main(String[] args) {
Thread t=new DataThread();
t.setName("备份数据线程");
// 启动线程之前,将线程设置为守护线程
t.setDaemon(true);
t.start();
// 主线程:主线程是用户线程
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"----"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class DataThread extends Thread{
public void run(){
int i=0;
// 即使是死循环,但由于该线程是守护者,当用户线程结束,守护线程自动终止。
while(true){
System.out.println(Thread.currentThread().getName()+"---"+(++i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 实现线程的第三种方式:
* 实现Callable接口
* 这种方式的优点:可以获取到线程的执行结果。
* 这种方式的缺点:效率比较低,在获取t线程执行结果的时候,当前线程受阻塞,效率较低。
*/
public class ThreadTest14 {
public static void main(String[] args) {
// 第一步:创建一个“未来任务类”对象。
// 参数非常重要,需要给一个Callable接口实现类对象。
FutureTask task=new FutureTask(new Callable(){
//call()方法相当于run()方法
public Object call(){
// 线程执行一个任务,执行之后可能会有一个执行结果
// 模拟执行
System.out.println("call method begin");
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("call method end");
int a=1;
int b=2;
return a+b;
}
});
Thread t=new Thread(task);
System.out.println("main begin");
t.start();
Object obj= null;
try {
// 这里是main方法,这是在主线程中。
// 在主线程中,怎么获取t线程的返回结果?
// get()方法的执行会导致“当前线程阻塞”
obj = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("线程执行结果"+obj);
// main方法这里的程序要想执行必须等待get()方法的结束
// 而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
// 另一个线程执行是需要时间的。
System.out.println("这段文字什么时候执行");
}
}
执行结果:
import java.util.ArrayList;
import java.util.List;
/**
* 1、使用wait方法和notify方法实现“生产者和消费者模式”
*
* 2、什么是“生产者和消费者模式”?
* 生产线程负责生产,消费线程负责消费。
* 生产线程和消费线程要达到均衡。
* 这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。
*
* 3、wait和notify方法不是线程对象的方法,是普通java对象都有的方法。
*
* 4、wait方法和notify方法建立在线程同步的基础之上。因为多线程要同时操作一个仓库。有线程安全问题。
*
* 5、wait方法作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁。
*
* 6、notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。
*
* 7、模拟这样一个需求:
* 仓库我们采用List集合。
* List集合中假设只能存储1个元素。
* 1个元素就表示仓库满了。
* 如果List集合中元素个数是0,就表示仓库空了。
* 保证List集合中永远都是最多存储1个元素。
*
* 必须做到这种效果:生产1个消费1个。
*/
public class ThreadTest15 {
public static void main(String[] args) {
List list=new ArrayList();
Thread t1=new Thread(new Producer(list));
Thread t2=new Thread(new Consumer(list));
t1.setName("生产者线程");
t2.setName("消费者线程");
t1.start();
t2.start();
}
}
//生产线程
class Producer implements Runnable{
//仓库
private List list;
public Producer(List list) {
this.list = list;
}
@Override
public void run() {
// 一直生产(使用死循环来模拟一直生产)
while(true){
// 给仓库对象list加锁。
synchronized(list){
// 大于0,说明仓库中已经有1个元素了。
if(list.size()>0){
try {
// 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁。
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 程序能够执行到这里说明仓库是空的,可以生产
Object obj=new Object();
list.add(obj);
System.out.println(Thread.currentThread().getName()+"---"+obj);
//唤醒消费者进行消费
list.notifyAll();
}
}
}
}
//消费线程
class Consumer implements Runnable{
private List list;
public Consumer(List list) {
this.list = list;
}
public void run(){
//一直消费
while(true){
synchronized(list) {
if (list.size() == 0) {
try {
// 仓库已经空了。
// 消费者线程等待,释放掉list集合的锁
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 程序能够执行到此处说明仓库中有数据,进行消费。
Object obj = list.remove(0);
System.out.println(Thread.currentThread().getName() + "---" + obj);
//唤醒生产者生产
list.notifyAll();
}
}
}
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
* 使用定时器指定定时任务。
* 死锁问题
* synchronized出现在静态方法上是找类锁。
*/
public class TimerTest {
public static void main(String[] args) {
// 创建定时器对象
Timer timer=new Timer();
//守护线程的方式
Timer timer1=new Timer(true);
try {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
Date firstTime=sdf.parse("2021-03-25 19-06-30 111");
// 指定定时任务
//timer.schedule(定时任务, 第一次执行时间, 间隔多久执行一次);
timer.schedule(new LogTimerTask(),firstTime,5000);
/*timer.schedule(new TimerTask(){
public void run(){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
String time=sdf.format(new Date());
System.out.println(time+":成功完成一次数据备份");
}
},firstTime,5000);*/
} catch (ParseException e) {
e.printStackTrace();
}
}
}
// 编写一个定时任务类
// 假设这是一个记录日志的定时任务
class LogTimerTask extends TimerTask {
public void run(){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
String time=sdf.format(new Date());
System.out.println(time+":成功完成一次数据备份");
}
}