目录

线程基本概念

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<V> 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<String> list = new ArrayList<String>();
Thread thread1 = new Thread(new Consumer(list), "消费者线程");
Thread thread2 = new Thread(new Producer(list), "生产者线程");
thread1.start();
thread2.start();
}
} //消费者线程
class Consumer implements Runnable {
//仓库
private List<String> 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<String> 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++;
}
}

java线程基础知识整理的更多相关文章

  1. java部分基础知识整理----百度脑图版

    近期发现,通过百度脑图可以很好的归纳总结和整理知识点,本着学习和复习的目的,梳理了一下java部分的知识点,不定期更新,若有不恰之处,请指正,谢谢! 脑图链接如下:java部分基础知识整理----百度 ...

  2. java线程基础知识----线程与锁

    我们上一章已经谈到java线程的基础知识,我们学习了Thread的基础知识,今天我们开始学习java线程和锁. 1. 首先我们应该了解一下Object类的一些性质以其方法,首先我们知道Object类的 ...

  3. java线程基础知识----线程基础知识

    不知道从什么时候开始,学习知识变成了一个短期记忆的过程,总是容易忘记自己当初学懂的知识(fuck!),不知道是自己没有经常使用还是当初理解的不够深入.今天准备再对java的线程进行一下系统的学习,希望 ...

  4. Java线程基础知识(状态、共享与协作)

    1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...

  5. java线程基础知识----java daemon线程

    java线程是一个运用很广泛的重点知识,我们很有必要了解java的daemon线程. 1.首先我们必须清楚的认识到java的线程分为两类: 用户线程和daemon线程 A. 用户线程: 用户线程可以简 ...

  6. Java 线程基础知识

    前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...

  7. java线程基础知识----java线程模型

    转载自http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html 1. 概述 多任务和高并发是衡量一台计算机处理器的能力重要指标 ...

  8. java线程基础知识----SecurityManager类详解

    在查看java Thread源码的时候发现一个类----securityManager,虽然很早就知道存在这样一个类但是都没有深究,今天查看了它的api和源码,发现这个类功能强大,可以做很多权限控制策 ...

  9. Java正则表达式基础知识整理

    指定为字符串的正则表达式必须首先被编译为此类的实例.然后,可将得到的模式用于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配.执行匹配所涉及的所有状态都驻留在匹配器中,所以多个 ...

  10. Kali Linux渗透基础知识整理(二)漏洞扫描

    Kali Linux渗透基础知识整理系列文章回顾 漏洞扫描 网络流量 Nmap Hping3 Nessus whatweb DirBuster joomscan WPScan 网络流量 网络流量就是网 ...

随机推荐

  1. hive之数据导入导出

    hive数据导入导出 一.导入数据4种方式 建表语句 create table test( name string, friends array, children map<string, in ...

  2. 华为路由器RIP路由协议配置命令

    RIP路由协议配置 rip 创建开启协议进程 network + ip 对指定网段接口使能RIP功能IP地址是与路由器直连的网段 debugging rip 1 查看RIP定期更新情况 termina ...

  3. 细聊.Net Core中IServiceScope的工作方式

    前言 自从.Net Core引入IOC相关的体系之后,关于它的讨论就从来没有停止过,因为它是.Net Core体系的底层框架,你只要使用了.Net Core的时候就必然会用到它.当然关于使用它的过程中 ...

  4. Mysql 用户远程登录数据库

    其实这个技术不难.我只是站在巨人的肩膀上.梳理一下我遇见的问题. 方法有两种.修改当前用户的host 为 %.或者直接授权(推荐) 直接授权: # mysql -u root -proot  // 登 ...

  5. MySQL 全局锁、表级锁、行级锁,你搞清楚了吗?

    大家好,我是小林. 最近重新补充了<MySQL 有哪些锁>文章内容: 增加记录锁.间隙锁.net-key 锁 增加插入意向锁 增加自增锁为 innodb_autoinc_lock_mode ...

  6. vue3渲染函数(h函数)的变化

    vue3 渲染函数(h函数)的更改 h函数的更改总结 1==>h 现在全局导入,而不是作为参数传递给渲染函数 2==>渲染函数参数更改为在有状态组件和函数组件之间更加一致 3==>v ...

  7. 一个实用的 vite + vue3 组件库脚手架工具,提升开发效率

    无论是 vue2 全家桶还是 vue3 + vite + TypeScript,组件库的使用几乎大家都会,但自己开发一个独立组件库就不是每个人都掌握的,因为搭建组件库的基础开发环境,就会让很多同学望而 ...

  8. 基于docker和cri-dockerd部署kubernetes v1.25.3

    基于docker和cri-dockerd部署kubernetes v1.25.3 1.环境准备 1-1.主机清单 主机名 IP地址 系统版本 k8s-master01 k8s-master01.wan ...

  9. 使用 Go HTTP 框架 Hertz 进行 JWT 认证

    前言 上一篇文章简单介绍了一个高性能的 Go HTTP 框架--Hertz,本篇文章将围绕 Hertz 开源仓库的一个 demo,讲述如何使用 Hertz 完成 JWT 的认证与授权流程. 这里要说明 ...

  10. 你听说过OTA吗?

    以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「englyf」https://mp.weixin.qq.com/s/M660Sc4ey1SxCpRT6vcM9g what? 不知有多少 ...