多线程之间的通信问题
package lesson13;
class Resource{
String name;
String sex;
}
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
@Override
public void run() {
int x = 0;
while(true){
if(x==0){
r.name = "mike";
r.sex = "male";
}
else{
r.name = "J";
r.sex = "female";
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
Resource r ;
Output(Resource r){
this.r = r;
}
@Override
public void run() {
while (true){
System.out.println(r.name+"--"+r.sex);
}
}
}
public class test1 {
public static void main(String args[]){
Resource r = new Resource();
Input i = new Input(r);
Output o = new Output(r);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
<<<
J--female
mike--male
J--male
J--male
J--female
J--female
mike--female
mike--male
J--male
J--female
mike--female
mike--female
mike--male
mike--male
mike--female
J--male
为什么J的性别变成了male?
因为当Input获得执行权时,此时已经修改了name为J,然后立刻失去了执行权,此时Output获得了执行权,然后就打印了J–male.
要怎样解决这个问题?
package lesson13;
class Resource{
String name;
String sex;
}
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
@Override
public void run() {
int x = 0;
while(true){
synchronized (r) {
if (x == 0) {
r.name = "mike";
r.sex = "male";
} else {
r.name = "J";
r.sex = "female";
}
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
Resource r ;
Output(Resource r){
this.r = r;
}
@Override
public void run() {
while (true){
synchronized (r){
System.out.println(r.name+"--"+r.sex);
}
}
}
}
public class test1 {
public static void main(String args[]){
Resource r = new Resource();
Input i = new Input(r);
Output o = new Output(r);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
<<<
mike--male
mike--male
mike--male
mike--male
mike--male
mike--male
J--female
J--female
J--female
此时正常了
等待唤醒机制
涉及的方法
- wait() :让线程处于冻结状态,被wait的线程会被存储到线程池中。
- notify(): 唤醒线程池中的一个线程(任意)。
- notifyAll():唤醒线程池中的所有线程。
这些方法都必须定义在同步中。
因为浙西方法适用于操作线程状态的方法。
必须要明确操作的是哪个线程
为什么操作线程的方法wait(),notify(),notifyAll()定义在Object中?
因为这些方法是监视器的方法,监视器其实就是锁。
可以是任意的对象,任意的对象调用的方式一定定义在Object类中。
package lesson13;
class Resource{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
@Override
public void run() {
int x = 0;
while(true){
synchronized (r) {
if(r.flag){
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (x == 0) {
r.name = "mike";
r.sex = "male";
} else {
r.name = "J";
r.sex = "female";
}
r.flag = true;
r.notify();
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
Resource r ;
Output(Resource r){
this.r = r;
}
@Override
public void run() {
while (true){
synchronized (r){//官方文档称r为监视器(锁)
if(!r.flag){
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(r.name+"--"+r.sex);
r.flag = false;
r.notify();
}
}
}
}
public class test1 {
public static void main(String args[]){
Resource r = new Resource();
Input i = new Input(r);
Output o = new Output(r);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
<<<
J--female
mike--male
J--female
mike--male
J--female
mike--male
J--female
mike--male
r.wait()表示将该线程放入r的线程池中。
此处的flag是标识读函数是否读取信息。
package lesson13;
class Resource{
private String name;
private String sex;
private boolean flag = false;
//此时属性变成了私有属性,我们需要提供对外的方法来修改这些属性
// 但是需要注意的是,我们需要对这个方法进行同步操作,以防修改了姓名没有修改性别
// 输出函数亦是如此
public synchronized void set(String name,String sex){
if (flag){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = true;
notify();
}
public synchronized void get() {
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"---"+sex);
flag = false;
notify();
}
}
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
@Override
public void run() {
int x = 0;
while(true){
if (x == 0) {
r.set("mike","male");
} else {
r.set("J","female");
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
Resource r ;
Output(Resource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.get();
}
}
}
public class test1 {
public static void main(String args[]){
Resource r = new Resource();
Input i = new Input(r);
Output o = new Output(r);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
<<<
J---female
mike---male
J---female
mike---male
J---female
mike---male
J---female
mike---male
如果有多个生产者消费者,或有什么情况发生?
package lesson13;
class newResource{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
flag = true;
notify();
}
public synchronized void get(){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"---消费者-----"+this.name);
flag = false;
notify();
}
}
class Producer implements Runnable{
private newResource r = new newResource();
Producer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.set("duck");
}
}
}
class Consumer implements Runnable{
private newResource r = new newResource();
Consumer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.get();
}
}
}
public class test2 {
public static void main(String args[]){
newResource r = new newResource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(p);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
会出现某个鸭子没有被消费,或者是某个鸭子被消费多次
因为如果t1生产完一只鸭子后,t2在等待被唤醒状态,因为随机唤醒一个线程,此时t2有生产一只鸭子,这是再唤醒t3,t3消费掉第二只,子则不会消费掉第一只鸭子。
那么如何避免这种情况呢?
这种情况出现的原因在于,判断flag后,直接进入睡眠状态,等被唤醒时,不再判断flag是否满足生产条件,就直接生产。
因此将if改为while,则生产线程被唤醒时,可以对其再次进行flag判断
package lesson13;
class newResource{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name){
while (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
flag = true;
notify();
}
public synchronized void get(){
while (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"---消费者-----"+this.name);
flag = false;
notify();
}
}
class Producer implements Runnable{
private newResource r = new newResource();
Producer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.set("duck");
}
}
}
class Consumer implements Runnable{
private newResource r = new newResource();
Consumer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.get();
}
}
}
public class test2 {
public static void main(String args[]){
newResource r = new newResource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(p);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
<<<
Thread-0---生产者---duck1
Thread-3---消费者-----duck1
Thread-0---生产者---duck2
Thread-1---消费者-----duck2
Thread-0---生产者---duck3
Thread-3---消费者-----duck3
但是此时又有可能引发死锁
t0生产完鸭子后,唤醒t1,t1不满足生产条件,唤醒t0,两个重复唤醒。死锁了
如何解决以上的问题?
没有特定的方法使t1来唤醒t2或者t3
于是我们使用notifyall()来会来唤醒所有的线程
package lesson13;
class newResource{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name){
while (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
flag = true;
notifyAll();
}
public synchronized void get(){
while (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"---消费者-----"+this.name);
flag = false;
notifyAll();
}
}
class Producer implements Runnable{
private newResource r = new newResource();
Producer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.set("duck");
}
}
}
class Consumer implements Runnable{
private newResource r = new newResource();
Consumer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.get();
}
}
}
public class test2 {
public static void main(String args[]){
newResource r = new newResource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(p);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
此时就不会出现问题了。但是降低了效率,因为所有的线程都被唤醒了
java 对于上述情况已经有了新的改进,将锁抽象为接口,并提供获取锁与释放锁的方法。
Condition将原来Object中的方法封装成了一个对象,之前synchronized同步代码块中只能有一组锁的操作(wait,notify)(同步只有一个锁),现在,一个lock锁对象可以挂载多个condition对象。
一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,请使用其newCondition()方法。
package lesson13;
import java.util.concurrent.locks.*;
class newResource{
private String name;
private int count = 1;
private boolean flag = false;
//创建一个锁对象
Lock lock = new ReentrantLock();
// 通过已有的锁获取该锁上的监视器对象
Condition con = lock.newCondition();
public void set(String name){
lock.lock();
try{
while (flag){
try {
con.await();//注意此处
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
flag = true;
con.signalAll();
}finally {
lock.unlock();//将锁释放
}
}
public synchronized void get(){
lock.lock();
try{
while (!flag){
try {
con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"---消费者-----"+this.name);
flag = false;
con.signalAll();
}finally {
lock.unlock();
}
}
}
class Producer implements Runnable{
private newResource r = new newResource();
Producer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.set("duck");
}
}
}
class Consumer implements Runnable{
private newResource r = new newResource();
Consumer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.get();
}
}
}
public class test2 {
public static void main(String args[]){
newResource r = new newResource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(p);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
目前一个锁监视了生产者与消费者。我们希望生产者能唤醒消费者,消费者能唤醒生产者。
package lesson13;
import java.util.concurrent.locks.*;
class newResource{
private String name;
private int count = 1;
private boolean flag = false;
// 通过已有的锁获取该锁上的监视器对象
Condition pro_con = lock.newCondition();
// 生产者锁
Condition con_con = lock.newCondition();
// 消费者锁
public void set(String name){
lock.lock();
try{
while (flag){
try {
pro_con.await();//注意此处
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
flag = true;
con_con.signal();//唤醒消费者
}finally {
lock.unlock();
}
}
public synchronized void get(){
lock.lock();
try{
while (!flag){
try {
con_con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"---消费者-----"+this.name);
flag = false;
pro_con.signalAll();
}finally {
lock.unlock();
}
}
}
class Producer implements Runnable{
private newResource r = new newResource();
Producer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.set("duck");
}
}
}
class Consumer implements Runnable{
private newResource r = new newResource();
Consumer(newResource r){
this.r = r;
}
@Override
public void run() {
while (true){
r.get();
}
}
}
public class test2 {
public static void main(String args[]){
newResource r = new newResource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(p);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
Lock接口:
替代了同步代码块,将同步的隐式操作转化为显示操作。
同时更为灵活,可以一个锁上加上多组监视器。
lock(): 获取锁
unlock():释放锁
Condition接口:
出现替代了Object中的wait,notify,notifyall方法。
将这些监视器方法单独进行了封装,变成了condition监视器对象。
await();
signgal();
signalAll();
wait 和sleep的区别?
1. wait 可以指定时间也可以不指定时间,sleep必须要指定时间
2. 在同步中时,对cpu的执行权和锁的处理不同
wait:释放执行权,释放锁。
sleep: 不释放执行权,不释放锁。
3.
当 t0,t1,t2都进入show的同步代码块时,都进行wait状态,而t4进入method时,唤醒了所有的线程,此时所有线程都具备了执行资格,但是为获取锁,因为t4并未释放锁。此时t0,t1,t2谁获取锁就执行谁。
停止线程
- stop方法。(已废弃)
- run 方法结束
怎么控制线程的任务结束?
在任务中都会有循环结构,只要控制住循环就可以结束循环。
package lesson13;
class stopThread implements Runnable{
private boolean flag = true;
@Override
public synchronized void run() {
while (flag){
System.out.println(Thread.currentThread().getName()+"-------");
}
System.out.println("over");
}
public void setFlag() {
this.flag = false;
}
}
public class test3 {
public static void main(String[] args) {
stopThread s = new stopThread();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.start();
for (int i = 0;;i++){
if (i==50){
s.setFlag();
break;
}
System.out.println(Thread.currentThread().getName()+""+i);
}
}
}
但是以上情况有停不下的时候
package lesson13;
class stopThread implements Runnable{
private boolean flag = true;
@Override
public synchronized void run() {
while (flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-------");
}
}
public void setFlag() {
this.flag = false;
}
}
public class test3 {
public static void main(String[] args) {
stopThread s = new stopThread();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.start();
for (int i = 0;;i++){
if (i==50){
s.setFlag();
break;
}
System.out.println(Thread.currentThread().getName()+""+i);
}
System.out.println("over");
}
}
此时主线程停了,而t0 和t1
为什么没用呢?
如果线程处于冻结状态就无法读取标记。
但是如何解决中这种情况呢?
Thread中的interrupt方法
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu执行资格。
强制动作会抛出InterruptedException,需要处理
package lesson13;
class stopThread implements Runnable{
private boolean flag = true;
@Override
public synchronized void run() {
while (flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"-------"+e);
}
System.out.println(Thread.currentThread().getName()+"-------");
flag=false;
}
}
public void setFlag() {
this.flag = false;
}
}
public class test3 {
public static void main(String[] args) {
stopThread s = new stopThread();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.start();
for (int i = 0;;i++){
if (i==50){
// s.setFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+""+i);
}
System.out.println("over");
}
}
<<<
main45
main46
main47
main48
main49
over
Thread-0-------java.lang.InterruptedException
Thread-0-------
Thread-1-------java.lang.InterruptedException
Thread-1-------
线程中其他常见的方法?
package lesson13;
class stopThread implements Runnable{
private boolean flag = true;
@Override
public synchronized void run() {
while (flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"-------"+e);
}
System.out.println(Thread.currentThread().getName()+"-------");
flag=false;
}
}
public void setFlag() {
this.flag = false;
}
}
public class test3 {
public static void main(String[] args) {
stopThread s = new stopThread();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.setDaemon(true);
t2.start();
for (int i = 0;;i++){
if (i==50){
t1.interrupt();//此时没有进行t2的强制中断操作。
break;
}
System.out.println(Thread.currentThread().getName()+""+i);
}
System.out.println("over");
}
}
<<<
main45
main46
main47
main48
main49
over
Thread-0-------java.lang.InterruptedException
Thread-0-------
也能正常结束
setDaemon将线程转化为守护进程,当前台线程都结束时,无论后台进程处于什么状态都会消失。
join方法
package lesson13;
class Demo implements Runnable{
@Override
public void run() {
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"----"+x);
}
}
}
public class test4 {
public static void main(String args[]) throws InterruptedException {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t1.join();
t2.start();
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"----"+x);
}
}
}
<<<
Thread-0----0
Thread-0----1
Thread-0----2
Thread-0----3
Thread-0----4
Thread-0----5
Thread-0----6
Thread-0----7
Thread-0----8
Thread-0----9
main----0
main----1
main----2
main----3
Thread-1----0
main----4
Thread-1----1
main----5
Thread-1----2
main----6
Thread-1----3
main----7
Thread-1----4
main----8
Thread-1----5
main----9
Thread-1----6
Thread-1----7
Thread-1----8
Thread-1----9
package lesson13;
class Demo implements Runnable{
@Override
public void run() {
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"----"+x);
}
}
}
public class test4 {
public static void main(String args[]) throws InterruptedException {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
t1.join();
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"----"+x);
}
}
}
<<<
Thread-0----0
Thread-1----0
Thread-0----1
Thread-1----1
Thread-0----2
Thread-1----2
Thread-0----3
Thread-1----3
Thread-0----4
Thread-1----4
Thread-0----5
Thread-1----5
Thread-0----6
Thread-1----6
Thread-0----7
Thread-1----7
Thread-0----8
Thread-1----8
Thread-0----9
Thread-1----9
main----0
main----1
main----2
main----3
main----4
main----5
main----6
main----7
main----8
main----9
Process finished with exit code 0
此时join值影响main线程等待t1执行结束后,再执行,不影响t2的执行。
package lesson13;
class Demo implements Runnable{
@Override
public void run() {
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"----"+x);
}
}
}
public class test4 {
public static void main(String args[]) throws InterruptedException {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
t1.join();
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread()+"----"+x);
}
}
}
<<<
Thread-1----0
Thread-0----0
Thread-1----1
Thread-0----1
Thread-1----2
Thread-1----3
Thread-1----4
Thread-0----2
Thread-1----5
Thread-0----3
Thread-1----6
Thread-0----4
Thread-0----5
Thread-1----7
Thread-0----6
Thread-1----8
Thread-0----7
Thread-1----9
Thread-0----8
Thread-0----9
Thread[main,5,main]----0
Thread[main,5,main]----1
Thread[main,5,main]----2
Thread[main,5,main]----3
Thread[main,5,main]----4
Thread[main,5,main]----5
Thread[main,5,main]----6
Thread[main,5,main]----7
Thread[main,5,main]----8
Thread[main,5,main]----9
Thread[main,5,main]----9 这里的5是优先级
serPriority()可以设置线程的优先级。
package lesson13;
class Demo implements Runnable{
@Override
public void run() {
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread()+"----"+x);
}
}
}
public class test4 {
public static void main(String args[]) throws InterruptedException {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.setPriority(Thread.MAX_PRIORITY);
t2.start();
for(int x = 0;x<10;x++){
System.out.println(Thread.currentThread()+"----"+x);
}
}
}
<<<
Thread[main,5,main]----0
Thread[main,5,main]----1
Thread[main,5,main]----2
Thread[main,5,main]----3
Thread[main,5,main]----4
Thread[main,5,main]----5
Thread[main,5,main]----6
Thread[main,5,main]----7
Thread[main,5,main]----8
Thread[main,5,main]----9
Thread[Thread-0,5,main]----0
Thread[Thread-0,5,main]----1
Thread[Thread-0,5,main]----2
Thread[Thread-1,10,main]----0
Thread[Thread-1,10,main]----1
Thread[Thread-1,10,main]----2
Thread[Thread-1,10,main]----3
Thread[Thread-1,10,main]----4
Thread[Thread-1,10,main]----5
Thread[Thread-1,10,main]----6
Thread[Thread-1,10,main]----7
Thread[Thread-1,10,main]----8
Thread[Thread-1,10,main]----9
Thread[Thread-0,5,main]----3
Thread[Thread-0,5,main]----4
Thread[Thread-0,5,main]----5
Thread[Thread-0,5,main]----6
Thread[Thread-0,5,main]----7
Thread[Thread-0,5,main]----8
Thread[Thread-0,5,main]----9
默认情况下,线程优先级为5,最高为10,优先级最高并不是只运行该线程,而是调用该线程的可能性大于其他线程。
面试题
class Demo implements Runnable{
@Override
public void run(Thread t) {
}
}
以上代码有错吗?如果有错编译时错误出现在哪一行?
第一行因为该类没有实现接口中定义的run方法(不能传参,传参相当于重载),
package lesson13;
public class test5 {
public static void main(String args[]){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runnable run");
}
})
{
public void run(){
System.out.println("subThread run");
}
}.start();
}
}
<<<
subThread run
package lesson13;
public class test5 {
public static void main(String args[]){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runnable run");
}
})
{
}.start();
}
}
<<<
runnable run
package lesson13;
public class test5 {
public static void main(String args[]){
new Thread()
{
}.start();
}
}
<<<