从java程序看多线程
真没把java当回事,但事实上学起来还是挺费劲的,要记的东西太多了,如何学呢?还是那句话,能学多少算多少,但看了不能白看,关键是要用起来!
如题:2022年4月,第31题:要求写出程序输出结果。
public class Test31 implements Runnable {//这里创建线程显然 不是继承Thread子类,而是通过实现Runnable接口,然后写run方法
public void run() {
try {
System.out.println("run()-<1>");
Thread.sleep(100);
System.out.println("run()-<2>");
} catch (InterruptedException x) {
System.out.println("run()-<3>");
return;
}
System.out.println("run()-<4>");
System.out.println("run()-<5>");
}
public static void main(String[] args) {
Test31 one = new Test31();//为什么java类中,可以自己new自己,实则是main比较特殊,程序的入口,编译时不考虑位置的(与这个类没有关联),这里写到类里,只是图方便,不需要import而矣。
Thread other = new Thread();//生成一个Thread实例,产生一个线程,
Thread third = new Thread(one);//初始化时传递的线程体,也就是方法
other.start();//调用了start方法,就进入了就绪态
third.start();
try {
System.out.println("run()-<6>");
Thread.sleep(200);
} catch (InterruptedException x) {
// System.out.println("run()-<8>");
}
System.out.println("main-<7>");
other.interrupt();//使用interrupt来终止线程,如果是终止阻塞态线程会抛出一个interruptException异常
}
}
程序运行结果,先运行主线程run()-<6>,再运行创建的线程,run()-<1>、run()-<2>、run()-<4>、run()-<5>,最后,主线程sleep(200)的时间到后,继承运行,输出run()-<7>.题目给出的other.interrupt并没有产生异常???
分析:
一直没学到这个层次,所以这块还是挺生的。有些东西是操作系统里边的,没必要重复,就从实际使用的角度出发,看看这些考点能在实际使用时怎么用?
基本知识,只列自己感觉生的或还不太理解的地方
线程的四种状态
新建,可运行态,阻塞态,死亡,这个与OS大同小异,了解下即可。关键是下面的如何用java实现这些状态?
java中线程的调度
java提供了一个线程调度器来监视和控制就绪状态的线程。调度采用的是抢占式,优先级高的线程优先执行。优先级数值越大,优先级越高(0~10)。java都有一个默认的主线程,也就是jvm启动的第一个线程,子线程是由应用程序创建的线程。优先级最低的线程是守护线程,用于监视其他线程工作的服务线程。
实现多线程的两种途径
一是继承 Thread 类,用 Thread 子类创建线程对象。二是在类中实现 Runnable接口,在类中提供 Runnable 接口的 run()方法。关键性工作有两个方面:一是编写线程的run()方法;二是建立线程实例。
继承Thread类创建线程
Thread类是用来创建线程和提供线程操作的类。分析其最复杂的构造方法:Thread(ThreadGroup g,Runnable target,String m),创建一个线程,名为 m,属于指定的线程组 g,target 是线程的目标,也就是要创建的线程,必须实现Runnable接口,在程序中实现run()方法;这个方法就是线程体,也就是要线程要实现的功能。
实际使用时,并不一定会调用这个最复杂的构造函数,也可能是最简单的无参构造函数,但流程是一样的。
使用模板:
public class ThreadClassTest10 {
static Athread threadA;
static Bthread threadB;
public static void main(String[] args) {
threadA = new Athread();
threadB = new Bthread();
threadA.start();
threadB.start();
}
}
class Athread extends Thread {
public void run() {
Date timeNow;//为了能输出当时的时间
for (int i = 0; i <= 5; i++) {
timeNow = new Date();//得到当前时间
System.out.println("我是 threadA:" + timeNow.toString());
try {
sleep(200);
} catch (InterruptedException e) {
}
}
}
}
class Bthread extends Thread{
public void run(){
Date timeNow;//为了能输出当时的时间
for(int i=0;i<=5;i++)
{
timeNow = new Date();//得到当前时间
System.out.println("我是 threadB:" +timeNow.toString());
try{sleep(100);}
catch(InterruptedException e){}
}
}
}
使用Runnable接口创建线程
如题目所示:声明一个Runnable接口类,类内实现run()方法,然后创建线程Thread(//传递这个接口类),启动线程即可。更常用:
// 定义一个可运行的类
public class MyRunnable implements Runnable {
public void run(){
}
}
// 创建线程对象
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();
线程挂起的几种方法
sleep()
是一个静态方法,也是可以引发InterruptException中断的,所以需要捕捉。
与yield方法的区别:
yield:只允许同优先级进程运行。
wait()
当前线程等待,直到其他线程调用该对象的notify()或notifyall()方法唤醒。
notify只是唤醒等待的第一个线程。
notifyall则唤醒等待的所有线程,重新参与竞争。
join()
将线程加入到当前正在执行的线程中先运行,如下例子:
public class ThreadJoinTest extends Thread {
public ThreadJoinTest() {
}
public void run() {
try {
for (int i = 0; i < 5; i++) {
System.out.println("running the first loop " + i);
}
Thread.sleep(1000);
for (int i = 6; i < 10; i++) {
System.out.println("running the second loop" + i);
}
} catch (InterruptedException ie) {
System.out.println("Sleep interrupted in run()");
}
}
public static void main(String[] args) {
try {
ThreadJoinTest ti = new ThreadJoinTest();
Thread t = new Thread(ti);
t.start();
t.join();//加入到主线程中运行,所以先要运行完t线程
for (int i = 11; i < 15; i++) {
System.out.println("running the third loop" + i);
}
} catch (InterruptedException ie) {
System.out.println("Join interrupted in run()");
}
System.out.println("Exiting from Main");
}
}
join是中断当前正在执行的线程,不一定非得是main线程,如下例子,便是先a,b再c,最后才是main:
public class ThreadJoin2Test extends Thread {
public ThreadJoin2Test(String a) {
super(a);//调用父类的方法
}
public void run() {
System.out.println(this.getName());
}
public static void main(String[] args) {
ThreadJoin2Test a = new ThreadJoin2Test("a");//创建一个名为a的子线程
ThreadJoin2Test b = new ThreadJoin2Test("b");
ThreadJoin2Test c = new ThreadJoin2Test("c");
a.start();
b.start();
c.start();
try {
c.join();
} catch (Exception e) {
}
System.out.println("This is Main!");
}
}
关于中断线程 interrupt
书中没有过多的介绍,但确实题目中已经涉及到了,可以看作是一个考点。看看实际中如何用?详见程序注释,这里是打断一个正在挂起的线程,引发打断异常。
public class ThreadInterruptTest extends Thread {
public ThreadInterruptTest() {
}
public void run() {
try {
for (int i = 0; i < 5; i++) {//打印完0~4
System.out.println("running the first loop " + i);
}
Thread.sleep(10000);//子线程挂起,这个休眠时间要比主线程长,所以切换到主线程运行
for (int i = 6; i < 10; i++) {
System.out.println("running the second loop" + i);
}
} catch (InterruptedException ie) {
System.out.println("Sleep interrupted in run()");
for (int i = 11; i < 15; i++) {
System.out.println("running the third loop" + i);
}
}
}
public static void main(String[] args) {
ThreadInterruptTest ti = new ThreadInterruptTest();
Thread t = new Thread(ti);
t.start();//通过Thread子类的方式,启用了一个线程
//Delay for a few seconds to let the other thread get going
try {
Thread.sleep(2500);//运行子线程
} catch (InterruptedException ie) {
System.out.println("Sleep interrupted in main()");
}
System.out.println("About to wake up the other thread");
t.interrupt();//打断当前正在休眠的子线程,这样的话会抛出一个子线程异常
System.out.println("Exiting from Main");//子线程异常执行完后,会返回到主线程执行,执行完结束
}
}
线程间同步
主要就是使用wait和notify方法。简单了解下就可以
wait()和notify()都属于Object类的,java中的对象都带有这两个方法。
要使用wait()和notify()都必须首先对“对象” 进行 synchronized 加锁,脱离 synchronized 使用 wait()和notify() 会直接抛出异常.
线程间互斥
对象锁定标志
用关键字volatile来声明一个共享数据(变量)。只作用于变量,使用范围小
用关键字synchronized(同步的)来声明操作共享数据的一个方法或一段代码。定义临界段
volatile
主要作用在保证变量在内存中的可见性,任何修改都写回到内存,永远保证内存中的值 是最新的。但不能保证原子性
public class ThreadVolitileTest {
static class MyTest {
public volatile int number = 0;
// public void incr(){//不加synchronized,结果可能会不正确
public synchronized void incr(){
number++;
}
}
public static void main(String[] args) {
MyTest myTest = new MyTest();
for (int i = 1; i <= 10; i++){
new Thread(() -> {
for (int j = 1; j <= 1000; j++){
myTest.incr();
}
}, "Thread"+String.valueOf(i)).start();
}
//等线程执行结束了,输出number值
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println("当前number:" + myTest.number);
}
}
synchronized使用
synchronized同步的都是对象,锁的也都是对象,如下例子:
public class ThreadSynchronizedTest {
public static void main(String[] args) {
SellThread sell = new SellThread();
Thread sell1 = new Thread(sell , "sellman1");
Thread sell2 = new Thread(sell , "sellman2");
Thread sell3 = new Thread(sell , "sellman3");
sell1.start();
sell2.start();
sell3.start();//创建了三个线程
}
}
class SellThread implements Runnable {
private int i = 20;
String a = "now ok!";
public void run() {
while (true) {
// synchronized (a) {//synchronized定义的是一个临界段
synchronized (this) {//synchronized定义的是一个临界段
if (i > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + " sell " + i--);
}
}
}
}
}