进程:就是应用程序在内存中分配的空间。(正在运行中的程序)
线程:是进程中负责程序执行的执行单元。也称为执行路径。
一个进程中至少有一个线程在负责该进程的运行。
如果一个进程中出现了多个线程,就称该程序为多线程程序。
举例:运动场——鸟巢。水立方。
多线程技术:解决多部分代码同时执行的需求。合理地使用CPU资源。
/*
* 多线程的运行根据cpu的切换完成的。怎么切换cpu说的算,所以多线程运行有一个随机性(cpu的快速切换造成的)。
* JVM中的多线程
* 至少有两个线程,一个是负责自定义代码运行的。这个从main方法开始执行的线程称之为主线程。
* 一个是负责垃圾回收的。
*
* 通过实验,会发现每次结果不一定相同,因为随机性造成的。
* 而且每一个线程都有运行的代码内容。这个称之为线程的任务。
* 之所以创建一个线程就是为了去运行指定的任务代码。
*
* 而线程的任务都封装在特定的区域中。
* 比如:
* 主线程运行的任务都定义在main方法中。
* 垃圾回收线程在收垃圾时都会运行finalize方法。
*/
class Demo
{
//定义垃圾回收方法
public void finalize()
{
System.out.println("demo ok");
}
}
class FinallyDemo
{
public static void main(String[] args)
{
new Demo();
new Demo();
new Demo();
System.gc();//启动垃圾回收器。不一定会执行,是否执行由CPU决定。
System.out.println("Hello World");
}
}
结果:
Hello World!
demo ok
demo ok
demo ok
或
Hello World!
demo ok
demo ok
或其他的……
如果
class FinallyDemo
{
public static void main(String[] args)
{
System.gc();//启动垃圾回收器。不一定会执行,是否执行由CPU决定。
new Demo();
new Demo();
new Demo();
System.out.println("Hello World");
}
}
结果:
Hello World!
因为执行太快了,所以没有demo ok了
class Demo
{
private String name;
Demo(String name)
{
this.name=name;
}
public void show()
{
for(int x=1;x<=10;x++)
{
System.out.println(name+"..."+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d1=new Demo("张三");
Demo d2=new Demo("李四");
d1.show();
d2.show();
}
}
如何建立一个执行路径?
(Java lang-Thread 线程 是程序中的执行线程。java虚拟机允许应用程序并发地运行多个执行线程。)
通过查阅api文档java.lang.Thread类。
该类的描述中有创建线程的两种方式:
一.继承Thread类。
1,继承Thread类。
2,覆盖run方法。
3,创建子类对象就是创建线程对象。
4,调用Thread类中的start方法就可以执行线程。并会调用run方法。
start()开启线程后,都会执行run方法。说明run方法存储的是线程要运行的代码。
所以,记住,自定义线程的任务代码都存储在run方法中。
class Demo extends Thread
{
//覆盖run方法。
public void run()
{
System.out.println("run run");
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d1=new Demo("张三");
Demo d2=new Demo("李四");
d1.start();
d2.start();
}
}
结果:
run run
run run
把上一节的代码用在线程上:
class Demo extends Thread
{
private String name;
Demo(String name)
{
this.name=name;
}
//覆盖run方法。
public void run()
{
show();
}
public void show()
{
for(int x=1;x<=10;x++)
{
System.out.println(name+"..."+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d1=new Demo("张三");
Demo d2=new Demo("李四");
d1.start();
d2.start();
}
}
结果之一(注意顺序):
李四...1
李四...2
李四...3
李四...4
李四...5
张三...1
李四...6
张三...2
李四...7
李四...8
李四...9
李四...10
张三...3
张三...4
张三...5
张三...6
张三...7
张三...8
张三...9
张三...10
再加上一点东西
lass ThreadDemo
{
public static void main(String[] args)
{
Demo d1=new Demo("张三");
Demo d2=new Demo("李四");
d1.start();
d2.start();
for(int x=1;x<=10;x++)
{
System.out.println("main+"+x);
}
}
}
结果:
main+1
李四...1
李四...2
张三...1
张三...2
李四...3
李四...4
李四...5
李四...6
main+2
main+3
main+4
main+5
main+6
main+7
main+8
main+9
main+10
李四...7
李四...8
李四...9
李四...10
张三...3
张三...4
张三...5
张三...6
张三...7
张三...8
张三...9
张三...10
调用start会开启线程,让开启的线程去执行run方法中的线程任务。
直接调用run方法,线程并未开启,去执行run方法的只有主线程。
线程本身有自己给自己定义名称,用getName()可以返回该名称。
“Thread-”
把上一节的代码修改成:
class Demo extends Thread
{
private String name;
Demo(String name)
{
this.name=name;
}
//覆盖run方法。
public void run()
{
show();
}
public void show()
{
for(int x=1;x<=10;x++)
{
System.out.println(this.getName()+"…"+name+"..."+x);//当前执行对象,Thread-0,1...
//System.out.println(Thread.currentThread().getName()+"…"+name+"..."+x);//当前执行路径 main(虽然我这里的程序这两种结果都一样,都是Thread-0,1...==)
}
}
}
main中不能直接用getName(),因为没有继承Thread类。
而currentThread()是Thread类中的返回对当前正在执行的线程对象的引用。而且是静态的,可以调用。
lass ThreadDemo
{
public static void main(String[] args)
{
Demo d1=new Demo("张三");//Thread-0
Demo d2=new Demo("李四");//Thread-1
d1.start();//start():两件事:1.开启线程,2.调用run方法。
d2.start();
for(int x=1;x<=10;x++)
{
System.out.println(Thread.currentThread().getName()+"----------main+"+x);
}
}
}
结果:
main----------main+1
Thread-1...李四...1
Thread-1...李四...2
Thread-0...张三...1
Thread-1...李四...3
main----------main+2
Thread-1...李四...4
Thread-0...张三...2
Thread-1...李四...5
main----------main+3
main----------main+4
Thread-1...李四...6
Thread-0...张三...3
Thread-1...李四...7
main----------main+5
Thread-1...李四...8
Thread-0...张三...4
Thread-0...张三...5
Thread-1...李四...9
main----------main+6
Thread-1...李四...10
Thread-0...张三...6
main----------main+7
Thread-0...张三...7
main----------main+8
Thread-0...张三...8
Thread-0...张三...9
main----------main+9
Thread-0...张三...10
main----------main+10
每个x是局部变量 所以不冲突
stop()已过时
class SaleTicket extends Thread
{
private int tickets=100;
//卖票的代码需要被多个线程执行,所以要将这些代码定义在线程任务中。run方法。
public void run()
{
while(true)//无限循环,写多线程的时候常见。
{
if(tickets>0)
//System.out.println(this.getName()+"..."+tickets--);//main
System.out.println(Thread.currentThread().getName()+"..."+tickets--);
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
//创建四个线程。会创建400张票。不合适,不建议票变成静态的,所以如何共享这100张票。需要将资源与线程分离。
//到api中查阅了第二种创建线程的方式。
SaleTicket t1=new SaleTicket();
SaleTicket t2=new SaleTicket();
SaleTicket t3=new SaleTicket();
SaleTicket t4=new SaleTicket();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
结果中每个线程都要从100张减起,于实际不合理。
内存图解:
如何改进:
class TicketDemo
{
public static void main(String[] args)
{
//创建线程,开启四次。
SaleTicket t1=new SaleTicket();
//错误
t1.start();
t1.start();
t1.start();
t1.start();
}
}
结果:
java.lang.IllegalThreadStateException //非法线程状态异常
//因为多次启动一个线程是非法的。
所以如何改进:
用static(不建议用)、单例。
用创建线程的第二种方法。
创建四个线程。会创建400张票。不合适,不建议票变成静态的,所以如何共享这100张票。需要将资源与线程分离。
到api中查阅了第二种创建线程的方式。
1.定义一个类实现Runnable。
2.覆盖Runnable接口中的run方法,将线程要运行的任务代码存储到该方法中。
3.通过Thread类创建线程对象,并将实现了Runnable接口的对象作为Thread类的构造函数的参数进行传递。
4.调用Thread类的start方法,开启线程。
class SaleTicket implements Runnable
{
private int tickets=10;
//卖票的代码需要被多个线程执行,所以要将这些代码定义在线程任务中。run方法。
public void run()
{
while(true)//无限循环,写多线程的时候常见。
{
if(tickets>0)
//System.out.println(this.getName()+"..."+tickets--);//main
System.out.println(Thread.currentThread().getName()+"..."+tickets--);
}
}
}
class TicketDemo2
{
public static void main(String[] args)
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
1.避免了继承Thread类的单继承的局限性。
2.Runnable接口出现更符合面向对象,将线程单独进行对象的封装。
3.Runnable接口的出现。降低了线程对象和线程任务的耦合性。
所以,以后创建线程都使用第二种方式。
产生的原因:
1.线程任务中有处理到共享的数据。
2.线程任务中有多条对共享数据的操作。
一个线程在操作共享数据的过程中,其他线程参与了运算,造成了数据的错误。
解决的思想:
只要能保证多条操作共享数据的代码在某一时间段,被一条线程所执行,在执行期间不允许其他线程参与运算。
如何保证:
用到了同步代码块。
synchronized(对象)
{
需要被同步的代码。
}
class SaleTicket implements Runnable
{
private int tickets=10;
//卖票的代码需要被多个线程执行,所以要将这些代码定义在线程任务中。run方法。
public void run()
{
while(true)//无限循环,写多线程的时候常见。
{
if(tickets>0)
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"..."+tickets--);
}
}
}
class TicketDemo3
{
public static void main(String[] args)
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
结果
出现负数
内存图解:
当tickets的数量被判断成1后,同时多进程,可能就判断不到,因此产生负数。
解决:
class SaleTicket implements Runnable
{
private int tickets=100;
//卖票的代码需要被多个线程执行,所以要将这些代码定义在线程任务中。run方法。
Object obj=new Object();
public void run()
{
while(true)//无限循环,写多线程的时候常见。
{
synchronized(obj)
{
if(tickets>0)
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"..."+tickets--);
}
}
}
}
class TicketDemo3
{
public static void main(String[] args)
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
可是结果还是有问题,稍后再做改进。
加上下面代码后的内存图解:
synchronized(对象)
{
需要被同步的代码。
}
被同步的代码只能让一个线程执行下来。相当于一个锁。
同步在目前情况下保证了一次只能有一个线程在执行。其他线程进不来。
这就是同步的锁机制。
好处:解决了多线程的安全问题。
弊端:降低效率。
有可能出现这样一种情况:
多线程安全问题出现后,加入了同步机制,没有想到,安全问题依旧,怎么办?
这时肯定是同步出了问题。
只要遵守了同步的前提,就可以解决。
同步的前提:
多个线程在同步中必须使用同一个锁。这才是对多个线程同步。
两个储户,到同一个银行存钱,每个人存了3次,一次100元。
1.描述银行。
2.描述储户任务。
分析多线程是否存在安全隐患。
1.线程任务中是否有共享的数据。
2.是否多条操作共享数据的代码。
class Bank
{
private int sum;
private Object obj=new Object();
public void add(int n)
{
synchronized(obj)
{
sum=sum+n;
try{Thread.sleep(10);}catch(Exception e){}//多测试几次
System.out.println("sum="+sum);
}
}
}
class Customer implements Runnable
{
private Bank b=new Bank();
public void run()
{
for(int x=0;x<3;x++)
{
b.add(100);
}
}
}
class ExtendsDemo3
{
public static void main(String[] args)
{
//1.创建任务对象。
Customer c=new Customer();
//创建2个线程。通过Thread类对象。
Thread t1=new Thread(c);
Thread t2=new Thread(c);
t1.start();
t2.start();
}
}
不加同步结果之一:
sum=200
sum=200
sum=300
sum=400
sum=500
sum=600
出现两次200,出现安全问题。
解决:
加同步
该安全隐患消失。
上一小节中的第一段代码
因为函数封装性,在这里可以把同步当成修饰符。
同步函数 其实就是在函数上加上了同步关键字进行修饰。
同步表现形式有两种:
1.同步代码块。
2.同步函数。
同步函数使用的锁是什么?
函数需要被对象调用,哪个对象不确定,但是都用this来表示。
所以同步函数使用的锁是this.
(这里指b)
class Bank
{
private int sum;
public synchronized void add(int n)
{
sum=sum+n;
try{Thread.sleep(10);}catch(Exception e){}//多测试几次
System.out.println("sum="+sum);
}
}
验证需求:
启动两个线程,一个线程负责执行同步代码块(使用明锁)。
另一个线程使用同步函数(使用this锁)。
两个执行的任务是一样的,都是卖票。如果他们没有使用相同的锁,说明他们没有同步。会出现数据错误。
怎么能让一个线程一直在同步代码块中,一个线程在同步函数呢?
可以通过切换的方式。
class SaleTicket implements Runnable
{
private int tickets=100;
//定义一个boolean标记。
boolean flag=true;
Object obj=new Object();
public void run()
{
if(flag)
while(true)//无限循环,写多线程的时候常见。
{
synchronized(obj)
{
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"...code..."+tickets--); }
}
}
else
while(true)
sale();
}
public synchronized void sale()
{
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"...function..."+tickets--); }
}
}
class ExtendsDemo3
{
public static void main(String[] args)
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
t.flag=false;
t2.start();
}
}
结果:只有function在运行。
原因:因为flag很快被转换为false.
Thread-0...function...100
Thread-1...function...99
Thread-1...function...98
Thread-1...function...97
Thread-1...function...96
Thread-1...function...95
Thread-1...function...94
Thread-1...function...93
Thread-1...function...92
Thread-1...function...91
Thread-1...function...90
Thread-1...function...89
Thread-1...function...88
Thread-1...function...87
Thread-1...function...86
Thread-1...function...85
Thread-1...function...84
Thread-1...function...83
Thread-1...function...82
Thread-1...function...81
Thread-1...function...80
Thread-1...function...79
Thread-1...function...78
Thread-1...function...77
Thread-1...function...76
Thread-1...function...75
Thread-1...function...74
Thread-1...function...73
Thread-1...function...72
Thread-1...function...71
Thread-1...function...70
Thread-1...function...69
Thread-1...function...68
Thread-1...function...67
Thread-1...function...66
Thread-1...function...65
Thread-1...function...64
Thread-1...function...63
Thread-1...function...62
Thread-1...function...61
Thread-1...function...60
Thread-1...function...59
Thread-1...function...58
Thread-1...function...57
Thread-1...function...56
Thread-1...function...55
Thread-1...function...54
Thread-1...function...53
Thread-1...function...52
Thread-1...function...51
Thread-1...function...50
Thread-1...function...49
Thread-1...function...48
Thread-1...function...47
Thread-1...function...46
Thread-1...function...45
Thread-1...function...44
Thread-1...function...43
Thread-1...function...42
Thread-1...function...41
Thread-1...function...40
Thread-1...function...39
Thread-1...function...38
Thread-1...function...37
Thread-1...function...36
Thread-1...function...35
Thread-1...function...34
Thread-1...function...33
Thread-1...function...32
Thread-1...function...31
Thread-1...function...30
Thread-1...function...29
Thread-1...function...28
Thread-1...function...27
Thread-1...function...26
Thread-1...function...25
Thread-1...function...24
Thread-1...function...23
Thread-1...function...22
Thread-1...function...21
Thread-1...function...20
Thread-1...function...19
Thread-1...function...18
Thread-1...function...17
Thread-1...function...16
Thread-1...function...15
Thread-1...function...14
Thread-1...function...13
Thread-1...function...12
Thread-1...function...11
Thread-1...function...10
Thread-1...function...9
Thread-1...function...8
Thread-1...function...7
Thread-1...function...6
Thread-1...function...5
Thread-1...function...4
Thread-1...function...3
Thread-1...function...2
Thread-1...function...1
解决方法:
加Thread.sleep(10);
class ExtendsDemo3
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
Thread.sleep(10); //加这行
t.flag=false;
t2.start();
}
}
结果:同步代码块用的是obj锁
同步函数用得是this锁。
如果想把这两个改为同一个锁:
把(obj)改为(this)
class SaleTicket implements Runnable
{
private int tickets=100;
//定义一个boolean标记。
boolean flag=true;
Object obj=new Object();
public void run()
{
if(flag)
while(true)//无限循环,写多线程的时候常见。
{
synchronized(this) //把obj改为this
{
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"...code..."+tickets--); }
}
}
else
while(true)
sale();
}
public synchronized void sale()
{
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"...function..."+tickets--); }
}
}
class ExtendsDemo3
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
Thread.sleep(10);
t.flag=false;
t2.start();
}
}
如果同步函数被静态修饰
这时的锁肯定不是this.
static方法随着类加载,这时不一定有该类的对象。但是一定有一个该类的字节码文件对象。
这个对象简单的表示方式就是 类名.class
属于 Class类型(描述类的类)
如下:
class SaleTicket implements Runnable
{
private int tickets=100;
//定义一个boolean标记。
boolean flag=true;
Object obj=new Object();
public void run()
{
if(flag)
while(true)//无限循环,写多线程的时候常见。
{
synchronized(SaleTicket.class) //把this改为SaleTicket.class
{
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"...code..."+tickets--);}
}
}
else
while(true)
sale();
}
public static synchronized void sale()
{
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"...function..."+tickets--);}
}
}
class ExtendsDemo3
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
Thread.sleep(10);
t.flag=false;
t2.start();
}
}
表达方式
Demo.class
Person.class
int.class
arr.class
//饿汉,相对于多线程并发,安全。
class Single {
//创建一个本类对象
static Single s=new Single();
//构造函数私有化
private Single() {}
//定义一个方法返回该对象。让其他程序可以获取到。
static Single getInstance()
{
return s;
}
}
//懒汉。延迟加载模式。
//在多线程并发访问时,会出现线程安全问题,所以要加同步。
//加了同步就可以解决问题。无论是同步函数,还是同步代码都行。
//但是,效率低了。
class Single {
//创建一个本类对象
private static Single s=new Single();
//构造函数私有化
private Single() {}
//定义一个方法返回该对象。让其他程序可以获取到。之所以定义访问,就是为了可控。
public static synchronized Single getInstance()//加同步
{
public static Single getInstance()
{
if(s==null)
s=new Single();//
return s;
}
}
class Demo implements Runnable
{
public void run()
{
Single.getInstance();
}
}
如何提高懒汉式的提高效率?用静态同步代码块。
可以通过if对单例对象的双重判断的形式。
class Single {
//创建一个本类对象
private static Single s=new Single();
//构造函数私有化
private Single() {}
//定义一个方法返回该对象。让其他程序可以获取到。之所以定义访问,就是为了可控。
public static Single getInstance()//加同步
{
if(s==null)
{
synchronized(Single.class)
{
public static Single getInstance()
{
if(s==null)
s=new Single();//
}
}
return s;
}
}
解析:
1.当线程0进去后,判断是否为空,为空。进入同步的锁,判断是否为空,为空,创建对象。
2.当线程1进去后,判断是否为空,为空,但因为同步,所以只能等线程0执行完被同步的部分时,再进去,再判断是否为空,不为空,不能创建对象。
3.当新的线程再进来时,判断是否为空,不为空,不创建对象。省去了同步的过程,因此提高效率。
同步代码块使用的任意的对象作为锁。
同步函数只能使用this作为锁,还有多个类中使用同一个锁。
如果说,一个类中只需要一个锁,这是可以考虑同步函数,使用this,写法简单。
但是一个类中如果需要多个锁,还有多个类中使用同一个锁。这时就只能使用同步代码块。
建议使用同步代码块。
场景一:
同步嵌套。
有两个锁,一个锁嵌套另一个锁。
下面代码中的两个锁分别为obj锁和this锁。
class SaleTicket implements Runnable
{
private int tickets=100;
//定义一个boolean标记。
boolean flag=true;
Object obj=new Object();
public void run()
{
if(flag)
while(true)//无限循环,写多线程的时候常见。
{
synchronized(obj) //obj lock
{
sale();
}
}
else
while(true)
sale();
}
public synchronized void sale() //this锁
{
synchronized(obj)
{
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e) {}//让线程到这里稍微停一下。
System.out.println(Thread.currentThread().getName()+"...function..."+tickets--);}
}
}
}
class DeadLockDemo
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//线程任务对象。
SaleTicket t=new SaleTicket();
//创建四个线程。通过Thread类对象。
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
Thread.sleep(10);
t.flag=false;
t2.start();
}
}
结果:
有时候不会运行到1.
所以尽量避免同步嵌套
以同步嵌套的死锁为例
class Task implements Runnable
{
private boolean flag;
Task(boolean falg)
{
this.flag=flag;
}
public void run()
{ if(flag)
{
while(true){
synchronized(MyLock.LOCKA)
{
System.out.println("if...locka");
synchronized(MyLock.LOCKB)
{
System.out.println("if...lockb");
}
}
}
}
else
{
synchronized (MyLock.LOCKB)
{
while(true){
System.out.println("else...lockb");
synchronized(MyLock.LOCKA)
{
System.out.println("else...locka");
}
}
}
}
}
}
class MyLock
{
public static final Object LOCKA=new Object();
public static final Object LOCKB=new Object();
}
class ExtendsDemo3
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//线程任务对象。
Task t1=new Task(true);
Task t2=new Task(false);
//创建四个线程。通过Thread类对象。
//Thread t1=new Thread(t);
//Thread t2=new Thread(t);
new Thread(t1).start();
new Thread(t2).start();
}
}
结果为什么只有else?
多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。
class Resource
{
private String name;//商品名称
private int count;//商品资源
//提供了给商品赋值的方法
public synchronized void set(String name)
{
this.name=name+"..."+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
}
//提供一个获取商品的方法
public synchronized void get()
{
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
}
}
//生产者
class Producer implements Runnable
{
private Resource r;
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
r.set("面包");
}
}
class Consumer implements Runnable
{
private Resource r;
Consumer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
r.get();
}
}
class ProducerConsumerDemo
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//1.创建资源。
Resource r=new Resource();
//2.创建两个任务。
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
//创建线程。
Thread t1=new Thread(pro);
Thread t2=new Thread(con);
t1.start();
t2.start();
}
}
结果:
和实际想要的生产出来的产品再被消费的顺序可以通过同步解决,但是大面积的生产然后再大面积地消费,不符合实际。
也就是说,通过同步解决了没生产就消费的问题。
但出现了连续的生产没有消费的情况,和需求中的生产一个,消费一个的情况不符。
使用了等待唤醒机制。
wait():该方法可以让线程处于冻结状态,并将线程临时存储到线程池中。
notify():唤醒指定线程池中的任意一个线程。唤醒等待的线程。
notifyAll():唤醒指定线程池中的所有线程。
先定义标记,如果标记为false,表示没有产品,此时需要生产者生产,消费者冻结,生产完后唤醒消费者。以此类推。
java文档中 wait在Thread类中
把上上节中的代码的第一部分改下即可。
//描述资源
class Resource
{
private String name;//商品名称
private int count=1;//商品资源
//定义标记
private boolean flag;
//提供了给商品赋值的方法
public synchronized void set(String name)
{
if(flag)
try{wait();}catch(InterruptedException e) {}//如果标记为true,则表示有产品,不需要生产,生产者需要再等一等。为false,就生产。
this.name=name+"..."+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
//生产完毕,将标记改为true。
flag=true;
//唤醒消费者
notify();
}
//提供一个获取商品的方法
public synchronized void get()
{
if(!flag)
try{wait();}catch(InterruptedException e) {}
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
//生产完毕,将标记改为false。
flag=false;
//唤醒生产者
notify();
}
}
注意点:
1.wait()、notify()、notifyAll()必须使用在同步之中,因为它们用来操作同步锁上的线程的状态的。
2.在使用这些方法时,必须标识它们所属于的锁,标识方式就是 锁对象.wait(),锁对象.notify(),锁对象.notifyAll()。
相同锁的notify,可以获取相同锁的wait();
3.省略锁对象时默认为this.
比如下图,4线程的lockb.notify只能唤醒2线程和3线程:
class ProducerConsumerDemo
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//1.创建资源。
Resource r=new Resource();
//2.创建两个任务。
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
//创建线程。
Thread t0=new Thread(pro);
Thread t1=new Thread(pro);
Thread t2=new Thread(con);
Thread t3=new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
结果中有出现类似
Thread-0...生产者..面包...32539
Thread-1...生产者..面包...32540
Thread-3...消费者..面包...32539
或者
Thread-0...生产者..面包...32539
Thread-2...消费者..面包...32539
Thread-3...消费者..面包...32539
产生问题1:
重复生产,重复消费。
具体:
notify唤醒的可能是任意一个等待的线程,假如唤醒的是生产者的第二个线程,这时不需要再经过标志判断和wait,直接运行下面的程序,因此会出现第一个结果。
第二个结果同理。
原因:
经过复杂的(等,资格)分析,发现被唤醒的线程没有判断标记就开始工作(生产/消费)了。
导致了重复的生产和消费的发生。
解决:
那就是被唤醒的线程必须判断标记。
使用while循环搞定。
但结果出现死锁现象。
产生问题2:
死锁了,所有的线程都处于冻结状态。
具体:
notify唤醒的可能是任意一个等待的线程,假如唤醒的是生产者的第二个线程,这时第二个线程要while循环,判断标记,因为生产的商品未被消费,因此2线程也处于等待状态。综上,四个线程都处于等待状态,因此处于死锁状态。
原因:
本方线程在唤醒时,又一次唤醒了本方线程,而本方线程循环判断标记,又继续等待,而导致所有的线程都等待了。
解决:
希望本方如果唤醒了对方线程,就可以解决。
可以使用notifyAll()方法。
虽然是全部唤醒,也就是说既有本方,又有对方。但是本方醒后,会判断标记继续等待。这样对方就有线程可以继续执行了。
综上:将if改为while循环,将notify()改为notifyAll();
这样已经实现了多生产多消费。
小缺陷:效率略低,因为notifyAll也唤醒了本方,做了不必要的判断。
1.5新增java.util.concurrent.locks
解决多生产多消费的效率问题。
使用了JDK1.5 java.util.concurrent.locks包中的对象
Lock接口:它的出现比synchronized有更多的操作。
lock():获取锁。
unlock():释放锁。
同步代码块或者同步函数的锁操作是隐式的。
使用了JDK1.5 Lock接口,按照面向对象的思想,将锁单独封装成了一个对象。
并提供了对锁的显示操作。
Lock接口就是同步的替代。
1.将线程中的同步更换为Lock接口的形式。
TIPs:
父类名 a = new 子类名()
子类名 b = new 子类名()
a只能调用父类的函数,不能调用子类的函数,因为它没有继承
b可以调用父类的函数也可以调用子类的函数
但是对构造函数的执行上二者是没有区别的。
如果获取锁和释放锁之间出现异常,那么异常下面的局部代码是不执行的,那么这个锁是不完整的。影响同步。
所以应该使用:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
把第一段代码改成
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.ReentrantLock;
//描述资源
class Resource
{
private String name;//商品名称
private int count=1;//商品资源
private Lock lock=new ReentrantLock();
//定义标记
private boolean flag;
//提供了给商品赋值的方法
public void set(String name)
{
//获取锁
lock.lock();
try {
while(flag)
try{wait();}catch(InterruptedException e) {}//如果标记为true,则表示有产品,不需要生产,生产者需要再等一等。
//为false,就生产。
this.name=name+"..."+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
//生产完毕,将标记改为true。
flag=true;
//唤醒消费者
notifyAll();}
finally {
//释放锁
lock.unlock();}
}
//提供一个获取商品的方法
public synchronized void get()
{
//获取锁
lock.lock();
try {
while(!flag)
try{wait();}catch(InterruptedException e) {}
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
//生产完毕,将标记改为false。
flag=false;
//唤醒生产者
notifyAll();}
finally {
//释放锁
lock.unlock();}
}
}
结果报错,因为wait(),notify()等只能用在同步函数和同步代码块中,
在这里wait没有了同步区域,没有了所属的同步锁。
不能用在这个Lock类中。
同步升级了。其中锁已经不是在任意对象,而是Lock类型的对象。
那么和任意对象绑定的监视器方法,是不是也升级了,有了专门和Lock类型锁的绑定的监视器方法呢?
查阅api。Condition接口替代了Object中的监视器方法。
以前监视器方法封装到每一个对象中。
现在将监视器方法封装到了Condition对象中。
方法名为:await signal signalAll
因此要用到Condition接口。
监视器对象Condition如何和Lock绑定呢?
可以通过Lock接口的newCondition()方法完成。
改进:修改监视器方法。
具体如下:
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.ReentrantLock;
//描述资源
class Resource
{
private String name;//商品名称
private int count=1;//商品资源
//创建新Lock。
private Lock lock=new ReentrantLock();
//创建和Lock绑定的监视器对象。
private Condition con=lock.newCondition();
//定义标记
private boolean flag;
//提供了给商品赋值的方法
public void set(String name)
{
//获取锁
lock.lock();
try {
while(flag)
try{con.await();}catch(InterruptedException e) {}//如果标记为true,则表示有产品,不需要生产,
//生产者需要再等一等。为false,就生产。
this.name=name+"..."+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
//生产完毕,将标记改为true。
flag=true;
//唤醒消费者
con.signalAll();}
finally {
//释放锁
lock.unlock();}
}
//提供一个获取商品的方法
public synchronized void get()
{
//获取锁
lock.lock();
try {
while(!flag)
try{con.await();}catch(InterruptedException e) {}
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
//生产完毕,将标记改为false。
flag=false;
//唤醒生产者
con.signalAll();}
finally {
//释放锁
lock.unlock();}
}
}
但是,问题依旧,一样唤醒了本方,效率仍旧低。
完整代码:
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.ReentrantLock;
//描述资源
class Resource
{
private String name;//商品名称
private int count=1;//商品资源
//创建新Lock。
private Lock lock=new ReentrantLock();
//创建和Lock绑定的监视器对象。创建两个。
//生产者监视器
private Condition producer_con=lock.newCondition();
//消费者监视器
private Condition consumer_con=lock.newCondition();
//定义标记
private boolean flag;
//提供了给商品赋值的方法
public void set(String name)
{
//获取锁
lock.lock();
try {
while(flag)
try{producer_con.await();}catch(InterruptedException e) {}//如果标记为true,则表示有产品,不需要生产,生产者需要再等一等。为false,就生产。
this.name=name+"..."+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
//生产完毕,将标记改为true。
flag=true;
//生产完毕,应该唤醒一个消费者来消费。
consumer_con.signal();}
finally {
//释放锁
lock.unlock();}
}
//提供一个获取商品的方法
public synchronized void get()
{
//获取锁
lock.lock();
try {
while(!flag)
try{consumer_con.await();}catch(InterruptedException e) {}
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
//消费完毕,将标记改为false。
flag=false;
//消费完毕,应该唤醒一个消费者来消费。
producer_con.signal();}
finally {
//释放锁
lock.unlock();}
}
}
//生产者
class Producer implements Runnable
{
private Resource r;
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
r.set("面包");
}
}
class Consumer implements Runnable
{
private Resource r;
Consumer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
r.get();
}
}
class ExtendsDemo3
{
public static void main(String[] args) throws InterruptedException //因为主函数不是覆盖函数,所以需要加抛出异常。
{
//1.创建资源。
Resource r=new Resource();
//2.创建两个任务。
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
//创建线程。
Thread t0=new Thread(pro);
Thread t1=new Thread(pro);
Thread t2=new Thread(con);
Thread t3=new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;//存、取、计数
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) //计数器是否到数组的长度
notFull.await();
items[putptr] = x; //把要存的放到数组中
if (++putptr == items.length) putptr = 0;//如果角标加一后为数组长度,那么把角标设为0,也就是说这次存完商品后要从头存起。
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr]; //从取的角标上拿该产品,也就是x。
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
相同:可以让线程处于冻结状态。
不同:
1.wait()可以指定时间,也可以不指定。
sleep()必须指定时间。
2.wait()释放CPU资源,释放锁。
sleep()释放CPU资源,不释放锁。
注意:
同步中是否只有一个线程运行。
是的。
但是如果存在wait(),他会出现多个情况,但是不需要担心数据错误的问题,是因为只有一个线程持有锁,执行完就会把锁放掉,其他的才有资格去执行。
synchronized(obj)
{
obj.wait();//t0.t1,t2
code...
}
synchronized(obj)
{
obj.notifyAll();//t3
}
异常会提示发生在那个线程上。
异常会结束线程任务,也就是可以结束所在线程。
演示1:
线程异常演示
class ThreadExceptionDemo
{
public static void main(String[] args)
{
int[] arr=new int[3];
System.out.print(arr[3]);
}
}
结果(异常在主线程中):
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at com.covnew.demo.ExtendsDemo3.main(ExtendsDemo3.java:9)
演示2:
class Demo implements Runnable
{
public void run()
{
System.out.println(4/0);
}
}
class ThreadExceptionDemo
{
public static void main(String[] args) throws Exception
{
new Thread(new Demo()).start();
Thread.sleep(10);
int[] arr=new int[3];
System.out.print(arr[4]);
System.out.print("over");
}
}
结果:
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at com.covnew.demo.Demo.run(SingleDemo.java:8)
at java.lang.Thread.run(Unknown Source)
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
at com.covnew.demo.ExtendsDemo3.main(ExtendsDemo3.java:11)
异常结束的是线程
over也不能输出。
1.搞定妖的问题。
分析:
1)共享数据。
2)线程任务中有多条操作共享数据的代码。
2.name和sex是私有的,需要在Res类中对外提供访问name和sex的方法。这个可以参照生产者消费者produceconsumerdemo.java。
3.实现间隔输出,使用等待唤醒机制produceconsumerdemo2.java。
一般情况下,需要判断条件。
解决一:
加了同步,问题依旧。看同步前提!
把第一个线程和第二个线程都用一个锁同步 synchronized(Resource.class){} 可以用静态锁,也可以用r,都是唯一的。实在找不到合适的锁,可以自己定义。如下面的代码。
class MyLock
{
public static final Object obj=new Object();
}
//描述资源
class Resource
{
String name;
String sex;
}
class Input implements Runnable
{
private Resource r;
Input(Resource r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
synchronized(MyLock.obj) {
if(x==0)
{
r.name="张三";
r.sex="男男男男男男男";
}
else
{
r.name="rose";
r.sex="women";
}
x=(x+1)%2;}
}
}
}
class Output implements Runnable
{
private Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
synchronized(MyLock.obj) {
System.out.println(r.name+"..."+r.sex);}
}
}
}
class Test
{
public static void main(String[] args) throws Exception
{
//1.创建资源。
Resource r=new Resource();
//2.创建两个任务。
Input pro=new Input(r);
Output con=new Output(r);
//创建线程。
Thread t0=new Thread(pro);
Thread t1=new Thread(con);
t0.start();
t1.start();
}
}
解决二:
//描述资源
class Resource
{
private String name;
private String sex;
public synchronized void set(String name,String sex)
{
this.name=name;
this.sex=sex;
}
public synchronized void out()
{
System.out.println(name+"..."+sex);
}
}
class Input implements Runnable
{
private Resource r;
Input(Resource r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
{
r.set("张三","男男男男男男男");
}
else
{
r.set("rose","women");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable
{
private Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class Test
{
public static void main(String[] args) throws Exception
{
//1.创建资源。
Resource r=new Resource();
//2.创建两个任务。
Input pro=new Input(r);
Output con=new Output(r);
//创建线程。
Thread t0=new Thread(pro);
Thread t1=new Thread(con);
t0.start();
t1.start();
}
}
解决三:
加标志和等待唤醒
/描述资源
class Resource
{
private String name;
private String sex;
//定义标记
private boolean flag;
public synchronized void set(String name,String sex)
{
if(flag)
try{this.wait();}catch(InterruptedException e){}
this.name=name;
this.sex=sex;
flag=true;
notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(name+"..."+sex);
flag=false;
this.notify();
}
}
class Input implements Runnable
{
private Resource r;
Input(Resource r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
{
r.set("张三","男男男男男男男");
}
else
{
r.set("rose","women");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable
{
private Resource r;
Output(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ExtendsDemo3
{
public static void main(String[] args) throws Exception
{
//1.创建资源。
Resource r=new Resource();
//2.创建两个任务。
Input pro=new Input(r);
Output con=new Output(r);
//创建线程。
Thread t0=new Thread(pro);
Thread t1=new Thread(con);
t0.start();
t1.start();
}
}
改进:
把代码的第一段改为:
//描述资源
class Resource
{
private String name;
private String sex;
//定义标记
private boolean flag;
//定义新锁。
private final Lock lock=new ReentrantLock();
//获取监视器对象
private Condition con=lock.newCondition();
public void set(String name,String sex)
{
lock.lock();
try {
while(flag)
try{con.await();}catch(InterruptedException e){}
this.name=name;
this.sex=sex;
flag=true;
con.signal();}
finally {
lock.unlock();}
}
public void out()
{
lock.lock();
try {
while(!flag)
try{con.await();}catch(InterruptedException e){}
System.out.println(name+"..."+sex);
flag=false;
con.signal();}
finally {
lock.unlock();}
}
}
如何停止线程?
原理:让run方法结束。
线程任务通常都有循环。因为开启线程就是为了执行需要一些时间的代码。
只要控制住循环,就可以结束run方法,就可以停止线程。
控制循环弄个标记即可。定义变量。
基础版
(去掉wait()部分即可,结果中有Thread-0或者Thread-1的运行结果),加上try{wait()}部分后,t1和t2都被冻结。
class StopThread implements Runnable
{
private boolean flag=true;
public synchronized void run()
{
while(flag)
{
try {
wait();//t1 t2
}
catch(InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+"..."+e.toString());
}
System.out.println(Thread.currentThread().getName()+"...hello");
}
}
public void changeFlag()
{
flag=false;
}
}
class ExtendsDemo3
{
public static void main(String[] args) throws Exception
{
StopThread st=new StopThread();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
t1.start();
t2.start();
for(int x=0;x<=50;x++)
{
if(x==40)
{
st.changeFlag();
}
System.out.println("main..."+x);
}
System.out.println("over");
}
}
结果:
main...0
main...1
main...2
main...3
main...4
main...5
main...6
main...7
main...8
main...9
main...10
main...11
main...12
main...13
main...14
main...15
main...16
main...17
main...18
main...19
main...20
main...21
main...22
main...23
main...24
main...25
main...26
main...27
main...28
main...29
main...30
main...31
main...32
main...33
main...34
main...35
main...36
main...37
main...38
main...39
main...40
main...41
main...42
main...43
main...44
main...45
main...46
main...47
main...48
main...49
main...50
over
把st.changeFlag();
改为
t1.interrupt();//将t1中断
t2.interrupt();//将t2中断
也相当于在System.out.println(Thread.currentThread().getName()+"..."+e.toString());
后加flag=false
然后这样的结果为:
main...0
main...1
main...2
main...3
main...4
main...5
main...6
main...7
main...8
main...9
main...10
main...11
main...12
main...13
main...14
main...15
main...16
main...17
main...18
main...19
main...20
main...21
main...22
main...23
main...24
main...25
main...26
main...27
main...28
main...29
main...30
main...31
main...32
main...33
main...34
main...35
main...36
main...37
main...38
main...39
main...40
main...41
main...42
main...43
main...44
main...45
main...46
main...47
main...48
main...49
main...50
over
Thread-0...java.lang.InterruptedException
Thread-0...hello
Thread-1...java.lang.InterruptedException
Thread-1...hello
因为interrupt的存在可以清除wait(),wait()无法执行就会catch到这个异常。也就是强行将线程从冻结中唤醒,然后执行异常的catch部分。下面的程序也就可以继续执行下去。
sleep()也可以,因为wait()和sleep()都被释放资格和执行权,如果没有其余合法的唤醒方式,interupt可以让他们重新获取到执行权。
(后台线程)
前台线程和后台线程在执行中没有区别,在结束时有区别。
没有定义后台线程的执行方式时,它会随着前台线程的结束而结束。
在java.lang中的Thread的方法摘要中。
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
on - 如果为 true,则将该线程标记为守护线程。
将上节第二段程序改为:
class ExtendsDemo3
{
public static void main(String[] args) throws Exception
{
StopThread st=new StopThread();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
t1.start();
t2.setDaemon(true);//将t2标记为后台线程。(守护线程)
for(int x=0;x<=50;x++)
{
if(x==40)
{
//st.changeFlag();
t1.interrupt();//将t1中断
//t2.interrupt();//将t2中断
}
System.out.println("main..."+x);
}
System.out.println("over");
}
}
当t1(前台线程)的冻结被打断后,继续运行到真正结束,t2(后台线程)也跟随着结束。
垃圾回收线程就是后台线程中的一种。
public final void join()
throws InterruptedException
等待该线程终止。
抛出:
InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的 中断状态 被清除。
join()的规律具体看这里
class Demo implements Runnable
{
public void run()
{
for(int x=0;x<40;x++)
{
System.out.println(Thread.currentThread().getName()+"..."+x);
}
}
}
class StopThreadDemo
{
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=1;x<=40;x++)
{
System.out.println("main..."+x);
}
}
}
结果:先执行完Thread-0的程序。
如果把join()放在另一个线程的下面。
class StopThreadDemo
{
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=1;x<=40;x++)
{
System.out.println("main..."+x);
}
}
}
结果:Thread-0和Thread-1共同抢资源。Thread-0和join容器息息相关,Thread-0结束,即容器死亡,主线程死亡。b随便。
1.优先级
先被切到的几率更大
toString()
:返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
如果把System.out.println(Thread.currentThread().getName()...);
改为System.out.println(Thread.currentThread().toString()...);
结果如:
Thread[Thread-1,5,main]...38
优先级:数字越大,优先级越高(1~10)
5是默认优先级。
setPriority(int newPriority)
:更改线程的优先级。
例如:
class StopThreadDemo
{
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.setPriority(Thread.MAX_PRIORITY);
for(int x=1;x<=40;x++)
{
System.out.println("main..."+x);
}
}
}
2.yield()
暂停当前正在执行的线程对象,并执行其他线程。
把第一段改为
class Demo implements Runnable
{
public void run()
{
for(int x=0;x<40;x++)
{
System.out.println(Thread.currentThread().toString()+"..."+x);
Thread.yield();
}
}
}
结果会分配得很杂乱,因为切换到一个线程,该现象就会很快被临时暂停,然后就会切换到其他线程。
3.Thread(ThreadGroup group, Runnable target, String name, long stackSize)
分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈大小。
线程组的中断
第一种:
class ExtendsDemo3
{
public static void main(String[] args)
{
new Thread() //Thread-0
{
public void run()
{
for(int x=1;x<=50;x++)
{
System.out.println("x="+x);
}
}
}.start();
Runnable r=new Runnable() //Thread-1
{
public void run()
{
for(int x=1;x<=50;x++)
{
System.out.println("y="+x);
}
}
};
new Thread(r).start();
//Thread t=new Thread(r).start();
//t.join();//等待该线程终止。
for(int x=1;x<=50;x++) //主线程
{
System.out.println("z="+x);
}
}
}
第二种(稍有难度):
class ExtendsDemo3
{
public static void main(String[] args)
{
//-----------稍有难度-------------------结果是什么?
new Thread(new Runnable()
{
public void run()
{
System.out.println("runnable run......");
}
})
{
public void run()
{
System.out.println("subThread...run...");
}
}.start();
}
}
结果:
subThread...run...
因为子类覆盖了父类。