Java语言系统 多线程.docx
《Java语言系统 多线程.docx》由会员分享,可在线阅读,更多相关《Java语言系统 多线程.docx(22页珍藏版)》请在冰豆网上搜索。
![Java语言系统 多线程.docx](https://file1.bdocx.com/fileroot1/2022-11/21/d85683ec-6cfc-474c-b9fd-7b6951bb1bd4/d85683ec-6cfc-474c-b9fd-7b6951bb1bd41.gif)
Java语言系统多线程
多线程
一、进程和线程的概念
1、进程:
运行中的程序。
一个进程中可以包含多个线程,可以同时运行。
2、进程:
执行中程序(程序是静态的概念,进程是动态的概念)。
3、多线程指的是在单个程序中可以同时运行多个不同的线程执行不同的任务。
多线程编程的目的就是“最大限度地利用CPU资源”,当某一线程的处理不需要占用CPU而只和IO等资源打交道时,让需要占用CPU资源的其他线程有机会获得CPU资源。
4、Java中如果我们自己没有产生线程,那么系统会给我们自动产生一个线程(主线程,main方法就在主线程上运行),我们的程序都是由线程来执行的。
二、线程的实现方式
●一个进程可以包含一个或多个线程
●一个程序实现多个代码同时交替运行就需要产生多个线程
●CPU随机的抽出时间,让我们的程序一会做这件事情,一会做另一件事情
1、线程的实现有两种方式:
第一种方式是继承Thread类,然后重写run方法;第二种是实现Runnable接口,然后实现run方法。
2、将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程。
start方法首先为线程的执行准备好系统资源,然后再去调用run方法。
当某个类继承了Thread类之后,该类就叫做一个线程类。
示例:
方法一:
继承Thread类
publicclassThreadTest{
publicstaticvoidmain(String[]args){
Thread1t1=newThread1();
t1.start();//必须调用start方法才能以线程的方式启动,run方法做不到
Thread2t2=newThread2();
t2.start();
}
}
classThread1extendsThread{
Stringname;
//Thread1(Stringname){
//this.name=name;
//}
@Override
publicvoidrun(){
for(inti=0;i<100;i++){
System.out.println("helloworld:
"+i);
}
}
}
classThread2extendsThread{
@Override
publicvoidrun(){
for(inti=0;i<100;i++){
System.out.println("helloccit:
"+i);
}
}
}
3、一个进程至少要包含一个线程
4、对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。
5、对于双核或双核以上的CPU来说,可以真正做到微观并行。
示例:
方法二:
实现Runnable接口
publicclassThreadTest2{
publicstaticvoidmain(String[]args){
MyThreadmt=newMyThread();
Threadt=newThread();//Threadt=newThread(newMyThread());
t.start();
}
}
classMyThreadimplementsRunnable{
@Override
publicvoidrun(){
for(inti=0;i<100;i++){
System.out.println("hello:
"+i);
}
}
}
改写成内部类:
publicclassThreadTest2{
publicstaticvoidmain(String[]args){
Threadt=newThread(newRunnable(){
@Override
publicvoidrun(){
for(inti=0;i<100;i++){
System.out.println("hello:
"+i);
}
}
});
t.start();
}
}
publicclassThreadTest2{
publicstaticvoidmain(String[]args){
Threadt=newThread(newMyThread());
t.start();
Threadt2=newThread(newMyThread2());
t2.start();
}
}
classMyThreadimplementsRunnable{
@Override
publicvoidrun(){
for(inti=0;i<100;i++){
System.out.println("hello:
"+i);
}
}
}
6、Thread类也实现了Runnable接口,因此实现Runnable接口中的run方法;
上例中:
publicclassThreadTest2{
publicstaticvoidmain(String[]args){
Threadt=newThread(newMyThread());
System.out.println(t.getName());
t.start();
Threadt2=newThread(newMyThread2());
System.out.println(t2.getName());
t2.start();
}
}
线程名字:
Thread-0
Thread-1
7、当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:
Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)
8、当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法此时什么事情也不做。
9、当使用第二种方式来生成线程对象时,我们需要实现Runnable接口的run方法,然后使用newThread(newMyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法就会调用MyThread类的run方法,这样我们自己编写的run方法就执行了。
publicclassThreadTest{
publicstaticvoidmain(String[]args){
Thread1t1=newThread1("firstthread");
t1.start();//必须调用start方法才能以线程的方式启动,run方法做不到
Thread1t2=newThread1("secondthread");
t2.start();
}
}
classThread1extendsThread{
//Stringname;
Thread1(Stringname){
super(name);
}
@Override
publicvoidrun(){
for(inti=0;i<10;i++){
System.out.println(this.getName()+i);
}
}
}
启动、睡眠
publicclassThreadTest{
publicstaticvoidmain(String[]args){
Thread1t1=newThread1("firstthread");
t1.start();//必须调用start方法才能以线程的方式启动,run方法做不到
}
}
classThread1extendsThread{
//Stringname;
Thread1(Stringname){
super(name);
}
@Override
publicvoidrun(){
for(inti=0;i<10;i++){
System.out.println(this.getName()+i);
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
}
课堂实践:
设计一个时钟显示器
三、线程的生命周期及优先级
1、创建状态:
new
2、可运行状态:
start
3、不可运行状态:
sleep,wait,阻塞等
4、消亡状态:
run方法结束后,自然消亡
线程的执行并不完全由优先级决定,更多的由底层的操作系统执行。
底层的操作系统会动态改变线程的优先级,等待时间长的优先级会增加,这个由操作系统决定。
最后一条主要在“分时系统”中。
四、线程的同步
publicclassThreadTest{
publicstaticvoidmain(String[]args){
Runnablerunnable=newHelloTread();
Threadt1=newThread(runnable);
Threadt2=newThread(runnable);
t1.start();
t2.start();
}
}
classHelloTreadimplementsRunnable{
inti;//成员变量,结果是50个
@Override
publicvoidrun(){
//inti=0;//局部变量:
和定义在成员变量时的i比较一下,结果是100个
while(true){
System.out.println("number:
"+i++);
try{
Thread.sleep((long)(Math.random()*1000));
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
if(50==i)break;
}
}
}
1、关于成员变量与局部变量,如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的,(出就是说一个线程对成员变量的改变会影响到另一个线程)。
2、如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响其他线程。
改变上例:
publicstaticvoidmain(String[]args){
Runnablerunnable=newHelloTread();
Threadt1=newThread(runnable);
runnable=newHelloTread();
Threadt2=newThread(runnable);
t1.start();
t2.start();
}
100个。
3、停止线程的方式不能使用Thread类的stop方法来终止线程的执行,一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。
4、不能依靠的优先级来决定线程的执行顺序。
示例:
银行取款
publicclassFetchMoney{
publicstaticvoidmain(String[]args){
Bankbank=newBank();
MoneyThreadt1=newMoneyThread(bank);
MoneyThreadt2=newMoneyThread(bank);
t1.start();
t2.start();
}
}
classBank{
privateintmoney=1000;
publicintgetMoney(intnumber){
if(number<=0){
return-1;
}elseif(number>this.money){
return-2;
}elseif(money<0){
return-3;
}
else{
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
money-=number;
System.out.println(money);
returnnumber;
}
}
}
classMoneyThreadextendsThread{
privateBankbank;
publicMoneyThread(Bankbank){
super();
this.bank=bank;
}
@Override
publicvoidrun(){
System.out.println(bank.getMoney(800));
}
}
最后剩-600.
改:
publicstaticvoidmain(String[]args){
Bankbank=newBank();
MoneyThreadt1=newMoneyThread(bank);
bank=newBank();
MoneyThreadt2=newMoneyThread(bank);
t1.start();
t2.start();
}
相当于2个账户,都剩200.
再改:
privatestaticintmoney=1000;
则要么,200,-600,要么-600,-600
在最初的代码中修改:
publicsynchronizedintgetMoney(intnumber)
结果:
200
800
-2
5、synchronized关键字:
当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
上锁解锁。
6、Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前那个线程执行方法完毕后或抛出了异常,那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
publicclassThreadTest4{
publicstaticvoidmain(String[]args){
Exampleexample=newExample();
Threadt1=newTheThread(example);
Threadt2=newTheThread2(example);
t1.start();
t2.start();
}
}
classExample{
publicsynchronizedvoidexecute(){
for(inti=0;i<10;i++){
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
System.out.println("example:
"+i);
}
}
publicsynchronizedvoidexecute2(){
for(inti=0;i<10;i++){
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
System.out.println("JAVA:
"+i);
}
}
}
classTheThreadextendsThread{
privateExampleexample;
publicTheThread(Exampleexample){
super();
this.example=example;
}
@Override
publicvoidrun(){
this.example.execute();
}
}
classTheThread2extendsThread{
privateExampleexample;
publicTheThread2(Exampleexample){
this.example=example;
}
@Override
publicvoidrun(){
this.example.execute2();
}
}
调用仍然顺序,因为是给对象上锁的。
7、如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕之前,其他线程是无法访问该对象的任何一个synchronized方法的(非synchronized方法不影响)。
改上例:
publicstaticvoidmain(String[]args){
Exampleexample=newExample();
Threadt1=newTheThread(example);
example=newExample();
Threadt2=newTheThread2(example);
t1.start();
t2.start();
}
则无序。
publicclassThreadTest4{
publicstaticvoidmain(String[]args){
Exampleexample=newExample();
Threadt1=newTheThread(example);
example=newExample();
Threadt2=newTheThread2(example);
t1.start();
t2.start();
}
}
classExample{
publicsynchronizedstaticvoidexecute(){
for(inti=0;i<10;i++){
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
System.out.println("example:
"+i);
}
}
publicsynchronizedstaticvoidexecute2(){
for(inti=0;i<10;i++){
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
System.out.println("JAVA:
"+i);
}
}
}
classTheThreadextendsThread{
privateExampleexample;
publicTheThread(Exampleexample){
super();
this.example=example;
}
@Override
publicvoidrun(){
this.example.execute();
}
}
classTheThread2extendsThread{
privateExampleexample;
publicTheThread2(Exampleexample){
this.example=example;
}
@Override
publicvoidrun(){
this.example.execute2();
}
}
1、如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
2、上一讲中同步上锁方法也可为如下方式:
synchronized块:
classExample2{
privateObjectobject=newObject();
publicvoidexecute(){
synchronized(object){//synchronized块,给object上锁
for(inti=0;i<10;i++){
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock