转自:https://blog.csdn.net/weixin_42032770/article/details/107536554?utm_medium=distribute.pc_category.none-task-blog-hot-8.nonecase&depth_1-utm_source=distribute.pc_category.none-task-blog-hot-8.nonecase&request_id=#%E7%BA%BF%E7%A8%8B%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5
Java基础知识总结
目录
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
3.1、sleep()
3.2、interrupt方法
3.3、stop方法
4、线程调度
4.1、常见的线程调度模型
4.2、java中提供的线程调度方法
4.3、线程让步
4.4、线程合并
4.5、线程安全
4.5.1、线程同步的实现
4.5.2、java中的线程安全性
4.5.3、synchronized总结
4.5.4、死锁
4.6、守护线程
4.6.1、守护线程的特点
4.6.2、一个简单的守护线程的例子
5、定时任务
5.1、实现一个定时器
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法
7.1、wait和notify方法介绍
7.2、生产者和消费者模式
7.3、实现奇偶数的交替输出
线程基本概念
1、什么是进程?什么是线程?
进程是一个应用程序,线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。在java语言中对于两个线程A和B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。 在使用了多线程机制之后,main()方法结束了,只是主线程结束了,主栈空了,但其他线程不一定结束,其他栈(线程)可能还在压栈弹栈。
1、java实现线程
java语言支持多线程机制。并且java已经实现了多线程(java.lang.Thread类和java.lang.Runnable接口)
第一种实现方式(继承java.lang.Thread类并重写run方法)
public class Thread_01 extends Thread {
@Override
public void run() {
super.run();
System.out.println(“第一个线程”);
}
public static void main(String[] args) {
Thread thread=new Thread(new Thread_01(),"first_thread"); //创建线程对象
thread.start(); //启动线程 start方法使线程处于就绪队列,等待CPU调用
}
}
第二种实现方式(实现java.lang.Runnable接口并实现run方法)
public class Thread_02 implements Runnable {
@Override
public void run() {
System.out.println(“第一个线程”);
}
public static void main(String[] args) {
Thread thread=new Thread(new Thread_01(),“first_thread”); //创建线程对象
thread.start(); //启动线程 start方法使线程处于就绪队列,等待CPU调用
}
}
通常使用第二种方法,因为一个类实现了接口还可以继承其他类
注意:start方法和run方法的区别!!!
run方法不会启动线程。
start方法的作用是:启动一个线程,在JVM中为线程开辟一个新的栈空间。之后start方法就结束了。线程启动成功并进入排队等待序列。等到被CPU调用到,就会自动调用run方法。
2、线程的生命周期
3、线程常用的方法
3.1、sleep()
public static native void sleep(long millis) throws InterruptedException;
作用:让线程进入休眠,进入“阻塞状态”。放弃占有CPU时间片,让其他线程使用。
public class Thread_03 {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Thread_03_1(),“Thread_03_1”);
thread.start();
int count=1; //计数器
while(true){
System.out.println(“Thread_03_1线程沉睡了”+count+++“秒”);
Thread.sleep(1000);
if(count>5){
break;
}
}
}
}
class Thread_03_1 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000*5); //让线程沉睡(等待) 5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
3.2、interrupt方法
interrupt方法可以中断线程的睡眠,依靠了java异常处理机制
public class Thread_03 {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Thread_03_1(),“Thread_03_1”);
thread.start();
int count=1; //计数器
while(true){
System.out.println(“Thread_03_1线程沉睡了”+count+++“秒”);
Thread.sleep(1000);
if(count==3){
System.out.println(“打断Thread_03_1睡眠”);
thread.interrupt();
break;
}
}
}
}
class Thread_03_1 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000*5); //让线程沉睡(等待) 5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+“醒了”);
}
}
线程Thread_03_1原计划沉睡5秒,在它睡到3秒时,使用interrupt方法打断其睡眠。
3.3、stop方法
stop方法可以强制终止一个线程的执行。不过这种方式容易丢失数据。因为这种方式会直接杀死线程,线程没有保存的数据会丢失。所以不建议使用
public class Thread_04 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Thread_04_1(), “Thread_03_1”);
thread.start();
//线程Thread_03_1执行5秒后,强制结束线程Thread_03_1
Thread.sleep(1000*6);
System.out.println(“强制终止线程Thread_03_1”);
thread.stop(); //强制终止线程
}
}
class Thread_04_1 implements Runnable{
@Override
public void run() {
for(int i=1;i<1000;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"–>"+i+“秒”);
}
}
}
建议使用如下方法结束一个线程:在线程类中增加一个布尔类型的变量run,通过改变run的值,来控制线程运行/停止状态。
public class Thread_05 {
public static void main(String[] args) throws InterruptedException {
Thread_05_1 t=new Thread_05_1();
Thread thread = new Thread(t, “Thread_05_1”);
thread.start();
//等候5秒之后,终止该线程
Thread.sleep(1000*5);
t.run=false;
System.out.println(thread.getName()+“线程已暂停”);
}
}
class Thread_05_1 implements Runnable{
boolean run=true; //通过引入一个布尔类型的变量,来标记该线程的状态(运行/停止)
@Override
public void run() {
for (int i = 1; i < 1000; i++) {
if (run) {
System.out.println(Thread.currentThread().getName() + "-->" + i + "秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
/**
* return就表示该线程结束了
* 如果有什么需要保存的,可以写在return之前
*/
return;
}
}
}
}
4、线程调度
4.1、常见的线程调度模型
抢占式调度模型、均分式调度模型
4.2、java中提供的线程调度方法
void setPriority(int newPriority) //设置线程优先级
int getPriority() //获取线程优先级
最低优先级:1 默认优先级:5 最高优先级:10
优先级高的线程抢占的CPU时间片就多一些,处于运行状态的时间片就多一些
public class Thread_06 {
public static void main(String[] args) {
Thread_06_1 t61=new Thread_06_1();
Thread_06_2 t62=new Thread_06_2();
Thread_06_3 t63=new Thread_06_3();
Thread thread1=new Thread(t61,“Thread_06_1”);
Thread thread2=new Thread(t62,“Thread_06_2”);
Thread thread3=new Thread(t63,“Thread_06_3”);
//设置线程优先级
thread1.setPriority(1); thread2.setPriority(2); thread3.setPriority(10);
thread1.start(); thread2.start(); thread3.start();
System.out.println(“主线程优先级为:”+Thread.currentThread().getPriority());
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"–>"+i);
}
}
}
class Thread_06_1 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+“优先级为:”+Thread.currentThread().getPriority());
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"–>"+i);
}
}
}
class Thread_06_2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
class Thread_06_3 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
4.3、线程让步
yield方法:使当前线程暂停,回到就绪状态,让给其他线程
public static native void yield();
public class Thread_07 {
public static void main(String[] args) {
Thread_07_1 tt=new Thread_07_1();
Thread thread=new Thread(tt,“Thread_07_1”);
thread.start();
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"–>"+i);
}
}
}
class Thread_07_1 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%10==0){
System.out.println(Thread.currentThread().getName()+"暂停了一下");
Thread.yield(); //让当前线程暂停一下
}
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
4.4、线程合并
join方法可以使得在t.join()中让CPU优先执行完t。将t合并到当前线程中,使当前线程受阻,t线程执行直到结束。
public class Thread_08 {
public static void main(String[] args) throws InterruptedException {
Thread_08_1 thread_08_1=new Thread_08_1();
Thread_08_2 thread_08_2=new Thread_08_2();
Thread thread=new Thread(thread_08_1,“Thread_08_1”);
Thread thread1=new Thread(thread_08_2,“Thread_08_2”);
thread.start();
//合并线程
thread.join(); //t合并到当前线程中,当前线程受阻塞,t线程执行直到结束
thread1.start();
thread1.join();
//Thread.currentThread().join();
System.out.println(“main over”);
}
}
class Thread_08_1 implements Runnable{
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
class Thread_08_2 implements Runnable{
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
join()的底层实现代码。
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
join()是在底层调用了wait方法,当主线程调用了thread.join()之后,主线程进入此方法,调用join()方法中的wait(0)方法,wait(0)表示无限等待直到被notify。即主线程会无限等待thread线程执行完成。
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException(“timeout value is negative”);
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
4.5、线程安全
数据在多线程并发的环境下会存在安全问题。例如如果多个用户想要修改某个共享的数据,就会引发线程安全问题。因此需要引入线程同步机制(即线程排队执行,不能并发)
4.5.1、线程同步的实现
java里面通过关键字synchronized给线程加锁。线程会获取锁,并独占cpu,只有当线程释放了锁之后,其余线程拿到锁之后才能运行。
当一个线程在运行状态时遇到synchronized关键字,该线程就会放弃占有的cpu时间片,在锁池里面找共享对象的对象锁。
一个线程同步synchronized的例子——模拟ATM机取款
Account类
package ATM;
/*
银行账户,使用线程同步机制,解决线程安全问题
*/
public class Account {
private String accountName; //账户名
private double remain; //余额
Object obj=new Object();
public Account(String accountName, int remain) {
this.accountName = accountName;
this.remain = remain;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public double getRemain() {
return remain;
}
public void setRemain(double remain) {
this.remain = remain;
}
//取款方法
public void withdraw(double money) {
//synchronized (obj){ //obj是一个全局变量,实例一次Account创建一个obj对象,是可以被共享的
/*Object obj1=new Object();
synchronized (obj1){*/ //obj1是一个局部变量,每调用一次run方法则会创建一个obj1对象,所以obj1不是共享对象
//synchronized (null){ //报错:空指针
//synchronized ("abc"){ //"abc"在字符串常量池当中,会让所有的线程都同步
synchronized (this){
double before = this.getRemain(); //取款之前的余额
double after = before - money; //取款之后的余额
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setRemain(after); //更新余额
}
}
}
AccountThread类
package ATM;
public class AccountThread extends Thread {
//两个线程必须共享同一个账户对象
private Account act;
private double money; //取款金额
//通过构造方法传递过来构造对象
public AccountThread(Account act,double money) {
this.act = act;
this.money=money;
}
@Override
public void run() { //run方法执行表示取款操作
act.withdraw(money);
System.out.println(Thread.currentThread().getName()+"在账户"+
act.getAccountName()+"取款"+money+"之后余额为:"+act.getRemain());
}
}
启动类:Test类
package ATM;
public class Test {
public static void main(String[] args) {
Account act=new Account(“act001”,10000); //创建账户对象
//创建两个线程,对同一账户取款
Thread threadA=new AccountThread(act,5000);
Thread threadB=new AccountThread(act,3000);
threadA.setName("小明");
threadB.setName("小华");
threadA.start();
threadB.start();
}
}
结果:
//synchronized可以用在实例方法上,此时锁是this
public synchronized void withdraw(double money) {
double before = this.getRemain(); //取款之前的余额
double after = before - money; //取款之后的余额
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setRemain(after); //更新余额
}
}
可以在实例方法上使用synchronized,synchronized出现在实例方法上,锁一定是this,不能是其他的对象了。所以这种方式不灵活。
另外还有一个缺点:synchronized出现在实例方法上,表示整个方法体都需要同步,可能会无故扩大同步的范围,导致程序的执行效率降低。所以这种方式不常用。
4.5.2、java中的线程安全性
java中有三大变量 :1、实例变量(在堆中) 2、静态变量(在方法中) 3、局部变量(在栈中)
局部变量不会有线程安全问题,因为局部变量不共享,局部变量在栈中,一个线程一个栈。
常量不会有线程安全问题,因为常量不会被改变。
实例变量在堆中,堆只有1个。静态变量在方法区中,方法区只有1个。由于堆和方法区都是多线程可共享的,所以实例变量和静态变量可能存在线程安全问题。
ArrayList、HashMap 、HashSet 是非线程安全的
Vector、Hashtable 是线程安全的
4.5.3、synchronized总结
synchronized有三种写法:
第一种:同步代码块(灵活)
synchronized(线程共享对象){
同步代码块;
}
第二种:在实例方法上使用 synchronized
表示共享对象一定是this,并且同步代码块是整个方法体。
第三种:在静态方法上使用 synchronized
表示找类锁。
类锁永远只有1把(就算创建了100个对象,那类锁也只有1把)
对象锁:1个对象1把锁,100个对象100把锁。 类锁:100个对象,也可能只是1把类锁。
package Synchronized;
//Q:doOther方法执行的时候需要等待doSome方法的结束吗?
//A:需要,因为静态方法是类锁,不管创建了几个对象,类锁只有1把
public class Exam01 {
public static void main(String[] args) throws InterruptedException {
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
Thread t1 = new Mythread(mc1);
Thread t2 = new Mythread(mc2);
t1.setName(“t1”);
t2.setName(“t2”);
t1.start();
Thread.sleep(1000);
t2.start();
}
}
class Mythread extends Thread {
private MyClass mc;
public Mythread(MyClass mc) {
this.mc = mc;
}
@Override
public void run() {
super.run();
if (Thread.currentThread().getName().equals("t1")) {
try {
mc.doSome();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (Thread.currentThread().getName().equals("t2")) {
mc.doOther();
}
}
}
class MyClass {
//synchronized出现在静态方法上是走 类锁
public synchronized static void doSome() throws InterruptedException {
System.out.println(“doSome begin”);
Thread.sleep(1000 * 5);
System.out.println(“doSome over”);
}
public synchronized static void doOther() {
System.out.println("doOther begin");
System.out.println("doOther over");
}
}
4.5.4、死锁
一个简单的死锁实现的例子
package Thread;
/*
实现一个死锁,
t1中obj1锁上后就睡了,t2中obj2锁上后就睡了。
t1想要释放obj1锁,就必须请求到obj2锁
t2想要释放obj2锁,就必须请求到obj1锁
t1与t2互相请求锁,但彼此都无法释放锁,所以形成了死锁
*/
public class Dead_lock {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
//t1,t2两个线程共享o1,o2
Thread t1 = new MyThread_1(obj1, obj2);
Thread t2 = new MyThread_2(obj1, obj2);
t1.start();
t2.start();
}
}
class MyThread_1 extends Thread {
Object obj1 = new Object();
Object obj2 = new Object();
public MyThread_1(Object obj1, Object obj2) {
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
synchronized (obj1) {
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
}
}
}
}
class MyThread_2 extends Thread {
Object obj1 = new Object();
Object obj2 = new Object();
public MyThread_2(Object obj1, Object obj2) {
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
synchronized (obj2) {
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1) {
}
}
}
}
注:synchronized在开发中最好不要嵌套使用,一不小心就可能导致死锁现象发生
4.6、守护线程
java语言中线程可分为两大类:
一类是:用户线程 例如:main方法主线程
一类是:守护线程(后台线程) 例如java垃圾回收线程
4.6.1、守护线程的特点
一般守护线程是一个死循环,所有用户线程只要结束,守护线程自动结束。
守护线程一般会用在一些定时任务,例如每天0点系统自动备份需要用到定时器,我们可以将定时器设置为守护线程一直在那里看着。每到0点就备份一次。所有的用户线程如果结束了,守护线程就自动退出。
4.6.2、一个简单的守护线程的例子
package Thread;
/**
-
用户线程备份数据,守护线程守护。
*/
public class GuardThread {
public static void main(String[] args) {
Thread thread = new BakDataThread();
thread.setName(“备份数据的线程”);//启动线程之前,将线程设置为守护线程 thread.setDaemon(true); thread.start(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }
}
}
class BakDataThread extends Thread {
@Override
public void run() {
int i = 0;
//即使是死循环,但由于该线程是守护者。当用户线程结束,守护线程自动终止
while (true) {
System.out.println(Thread.currentThread().getName() + “—>” + i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5、定时任务
5.1、实现一个定时器
package Thread;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String[] args) throws ParseException {
//创建定时器对象
Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
Date firstTime = sdf.parse(“2020-07-25 19:10:30”);
//指定定时任务
//timer.schedule(定时任务,第一次执行时间时间,间隔多久执行一次)
timer.schedule(new LogTimerTask(), firstTime, 1000 * 5);
}
}
//编写一个定时任务
class LogTimerTask extends TimerTask {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strTime = sdf.format(new Date());
System.out.println(strTime + ":成功完成了一次数据备份!");
}
}
6、通过Callable接口实现一个线程
使用Callable接口实现线程,可以获得该线程的返回值(JDK8新特性)
一个简单的实例:
package Thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThirdWay {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask(new Task());
Thread thread = new Thread(futureTask);
thread.start();
Object object = futureTask.get(); //通过get方法可以获取当前线程的返回值
主线程中这里的程序必须等待get()方法结束才执行
// get()方法为了拿另一个线程的执行结果需要等待其执行完成,因此要等待较长时间
System.out.print(“线程执行结果:”);
System.out.println(object);
}
}
class Task implements Callable {
@Override
public Object call() throws Exception {
System.out.println("call method begin");
Thread.sleep(1000 * 10);
System.out.println("call method end");
int a = 100;
int b = 200;
return a + b;
}
}
FutureTask类相关源码
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
Future类中实现了get方法获取传入线程的返回结果
7、Object类中的wait和notify方法
7.1、wait和notify方法介绍
wait和notify方法不是线程对象的方法,不能通过线程对象调用。
Object object=new Object();
object.wait();//object.wait()让正在object对象上活动的线程进入等待状态,无限等待,直到被唤醒为止
object.notify();//object.notify()唤醒正在object对象上等待的线程
object.notifyAll();//object.notifyAll唤醒正在object对象上等待的所有线程
7.2、生产者和消费者模式
一个简单的生产者和消费者实例
模拟生产者生产一个,消费者就消费一个。让仓库始终零库存。
package Thread;
import java.awt.;
import java.util.;
import java.util.List;
public class Producer_Consumer {
public static void main(String[] args) {
List list = new ArrayList();
Thread thread1 = new Thread(new Consumer(list), “消费者线程”);
Thread thread2 = new Thread(new Producer(list), “生产者线程”);
thread1.start();
thread2.start();
}
}
//消费者线程
class Consumer implements Runnable {
//仓库
private List list;
public Consumer(List<String> list) {
this.list = list;
}
@Override
public void run() {
//消费
while (true) {
synchronized (list) {
//如果仓库已经空了,消费者线程等待并释放list集合的锁
if (list.size() == 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//仓库中有商品,消费者进行消费
String str = list.remove(0);
System.out.println(Thread.currentThread().getName() + " 消费 " + str);
list.notify(); //唤醒生产者
}
}
}
}
//生产者线程
class Producer implements Runnable {
//仓库
private List list;
public Producer(List<String> list) {
this.list = list;
}
@Override
public void run() {
//生产
while (true) {
synchronized (list) {
//如果仓库里有东西,则停止生产。生产者线程等待并释放list集合的锁
if (list.size() > 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add("商品");
System.out.println(Thread.currentThread().getName() + " 生产 " + list.get(0));
list.notify(); //唤醒消费者
}
}
}
}
:
7.3、实现奇偶数的交替输出
package Thread;
/**
- 使用生产者和消费者模式实现两个线程交替输出:一个线程负责输出奇数,另一个线程负责输出偶数
*/
public class Number {
public static void main(String[] args) throws InterruptedException {
Num num = new Num(0);
Thread thread1 = new Thread(new Odd(num), “Odd”);
Thread thread2 = new Thread(new Event(num), “Event”);
thread1.start();
thread2.start();
}
}
class Odd implements Runnable {
Num num;
public Odd(Num num) {
this.num = num;
}
@Override
public void run() {
while (true) {
synchronized (num) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (num.getI() % 2 == 0) {
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
num.notifyAll();
}
}
}
}
class Event implements Runnable {
Num num;
public Event(Num num) {
this.num = num;
}
@Override
public void run() {
while (true) {
synchronized (num) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (num.getI() % 2 != 0) {
try {
num.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
num.notifyAll();
}
}
}
}
class Num {
private int i = 0;
public Num(int i) {
this.i = i;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
int printNum() {
return i++;
}
}
这里写自定义目录标题
欢迎使用Markdown编辑器
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎