多线程-------java版
来源:互联网 发布:mac上的windows虚拟机 编辑:程序博客网 时间:2024/04/28 21:47
一、 概念
线程是什么?
线程是一个进程的执行路径。多线程当然就是一个进行的多条执行路径。
什么是进程?
进程是一个程序的一个事例。
什么又是程序?
程序就是有组织的代码组成可以运行的文件。如.exe文件.class文件
为什么需要多线程?
当一个进程被阻塞的时候,我们需要另外一个进程来执行,这样就能提高cpu的利用率,让用户看起来跟并行运行一样。但是开辟一个新的进程需要维护很多资源。进城之间相互切换也是比较复杂。而且操作系统也不允许进程之间相互访问内存。也不能让他们相互访问内存,这样就会导致程序的紊乱。这时候就需要有多线程的出现。在一个进程中创建多个线程,线程之间切换消耗较小,而且共享内存区域。
总结一句:线程就是进程中不同的执行路径。
二、多线程的创建和启动:
Java为例:
一、首先需要有这个路径。
1.1、在java中线程类Thread。因此我们需要继承Thread类来让我们需要的类有开辟路径的功能。
class MyThread extends Thread{};
1.2、有了路径以后我们就需要写我们要在这个路径中做什么事情,比如说打印1到100,或者取余数等等操作。要重写Thread类中的public void run(){} 方法。
1.3、有了路径、有了能干的事,接下来我们就要在使用他的地方开启他就可以了。
MyThread myThread=new MyThread();
myThread.start();
这样当程序运行到myThread.start()。这里的时候操作系统就会知道你这里开辟了一个新的线程。等cpu有空的时候给你执行。注意:这里并不是立刻就执行自己创建的线程,而是等着,cpu给你分配“时间片“才能执行。
其实这句话应该理解为:操作系统告诉cpu这里有一个新的线程,你什么时候有空了就执行他一下。如果cpu没空也是不会执行新线程的,然后就执行run()方法。
如果直接调用MyThread类的run()方法,这叫做方法调用,而不是开启新线程。方法调用不会告诉cpu我这里有一个新的线程等着你执行。
二、2.1、建立路径:因为我们的目标是执行东西,干事情,因此我们并需要继承Thread类,实际上需要他的run()方法,那么第二种方式就是继承接口 Runnable,它里面只有一个run()方法。 Class MyThread implements Runnable{}.
2.2、Runnable接口中只有run()方法,这样我们重写run()方法即可。
Public voidrun(){};
2.3、有了路径有了事情,我们就需要开启路径了。也是start().方法。因为我们MyThead类中并没有start()方法,并不能没有通知cpu的功能。因此我们需要一个Thread一个对象。把该对象和继承Runnable接口对象相关联。
Thread t=newThread(MyThread);
t.start()。同样开辟并启动了一个新的线程。
三、Thread类的几个方法
线程启动以后我们需要控制线程,不能任由“它们”自由发挥。
线程的状态:
1.1、 就绪状态。此状态就是刚刚start()以后,可以随时等候cpu干活。
1.2、 运行状态。
1.3、 阻塞状态。当在运行之中产生了阻塞事件,那么就会从运行状态到阻塞状态。
当然还有创建和结束状态。
方法1:操作线程最多的是sleep().方法,该方法是一个静态方法。Thread.sleep()。参数为毫秒。1000毫秒=1秒。
在sleep()的时候可能被其他的线程所打扰,因此可能会产生“IntertuptedException”异常,这里最好用try catch一下。要不然会编译出错。
这里注意sleep进入的是“阻塞状态”。而并不是就绪状态。等他睡醒以后才是“就绪状态”
方法2: boolean isAlive()。非静态方法,判断线程是不是还“活着”。活着的状态有三种:就绪、运行、阻塞。
void join()。非静态方法。当调用某一个Thread对象的join方法就会把该对象的方法加入到调用该对象start()方法的线程中去运行,相当于了。方法调用,其实执行的过程就是类似于方法调用,必须等了join方法的线程执行完了,它依附的主线程才能执行。同样会抛出异常。
方法3:yield()方法,同样是静态方法。“高风亮节”。暂停当前的线程。它并不是进入“阻塞状态”。而是进入了就绪状态。它只是暂停一下,让出现在时间片。
注意这里都是Thread的方法,如果是从Runnable接口继承下来的线程是不能有上述的方法的。使用方法要分得清楚方法是那个类。
三、 前面提到,多线程可以共享一块内存,这就是产生了线程同步的问题。
线程同步是一个什么问题呢?
这里用一个例子说明:有个银行账户:里面有3000块。账户有存折和银行卡两种方式可以取钱。先用存折到柜台去取钱的时候,前台查看账户确实有3000块钱,已经出去2000块,还没有把账户余额改成1000,交到储户手里的时候,在拿着银行卡在自动取款机上取钱,同样取2000,自动取款机查看有3000块钱,这就把钱吐给你,并且把账户改成1000。而在柜台同样又开始进行,也把钱给你,把账户余额改成1000.这就取出了4000块。
这就是多线程带来最直接的问题。因为多线程可以同时访问同一块内存,也就是都能取钱。只是在其中一个取钱的时候,另一个线程打断了它。导致问题产生。而又不能不让其他线程使用。这样怎么办呢?
最简单的思路就是,我在取钱的时候别人不能动取钱的操作。等我取完钱了其他人才能有这个操作。也就是加一把锁子。
有两种方法:
1、 synchronized(this){}给当前操作次对象的操作加一个把锁。参数也可以使其他的对象。但是就是给其他的对象加锁了。
2、 另一个就是在方法前加上synchronized,当某一个线程在执行的时候其他线程是不进来的。
成这样的问题产生其实也可以说是,把本来不能再分的代码,被分开了。把这些代码用synchronized (this)括起来就行了。
四、 在三中提到了线程同步问题。那么线程同步真的就安全了吗?
答案当时是否定的,因为这样还会带来“死锁”的问题。
下面介绍“死锁”是怎么产生的。
线程A 、B。对象 a、b。
执行线程A的时候需要把a、b对象锁住、执行线程B的线程时候需要把b、a线程锁住。但是在执行A线程的锁a后线程被打断,执行了线程B。线程B执行到锁b,等着A线程执行完成使用a对象,而A线程同样等着B线程执行完成使用b线程。最后两个线程谁都不放,导致了线程的“死锁”。
--------------------------------------------------------------例子----------------------------------------
线程:一个程序中的不同执行路径。在一个程序中执行路径有多少条,过去写的都是一条执行路径,无论是调用方法,还是new一个类,都是一条执行路径main
进程:机器上个一个.class文件.exe文件都是一个进程。进程就是一个程序的一个事例。
cpu把时间分成时间片,在同一个时间点上只有一个线程在执行。速度快,逻辑上是并行,物理上仍然是只执行一个线程。
既然线程是程序的不同执行路径,那么怎么开辟一条新的路径?让一个新的路径执行起来?
java中多线程是同过java.lang.Thread类来实现的。每一个Thread类的对象就代表一个新的线程。有了新的线程那么必须有东西执行,在线程类的run方法中写
要执行的代码。写完之后那么就表明已经新的路径已经开辟好了。要想让它走起来,还要通过Thread对象的start()方法来启动线程。
为什么不直接调用run()方法呢?其实这就是开启线程和函数调用的区别。Thread类的start()方法告诉CPU“我现在有一个新的的线程,看你什么时候有时间给我分配点时间片过来”。
而直接写thread.run()(假设Threadthread=new Thread();)那么并没有创建线程。
创建线程的两种方法:
一、
1、写一个类来继承接口Runnable告诉编译器这是一个线程类。 class A implements Runnable{}
2、实现接口Runnable里面的方法run(),把新线程的功能写进去public void run(){}
3、创建一个线程对象来关联A类(A a=newA())。因为A类中没有start()方法,只有start()方法才能通知CPU这里有个新线程。Thread t =new Thread(a);
4、用start()方法开启新的线程。t.start();
二、
1、既然必须要用到start()方法,那么我们就直接继承Thread类。 class B extends Thread{}
2、同样实现类的publicvoid run()方法
3、Thread对象开启。Thread t=new Thread(); t.start();
无论是那种方法新线程的“分叉”都是从start()方法开始的,因为只有start()后cpu才会开始给新的线程“时间片”执行。
*/
public class TestThread1
{
publicstatic void main(String [] args)
{
Runner1r=new Runner1();
Threadt=new Thread(r);
t.start();
for(inti=0;i<100;i++)
{
System.out.println("MainThread:---------"+i);
}
}
}
classRunner1 implements Runnable
{
publicvoid run()
{
for(inti=0;i<100;i++)
{
System.out.println("Runner1Thread:---------"+i);
}
}
}
Sleep的使用:
/*Thread中的sleep方法是一个静态方法,用来让当前运行的进程“休眠”一段时间,参数是毫秒,1000毫秒就是1秒。
但是sleep()的使用是会抛出异常的,throwInterruptedException。而通常又是在run()方法中使用Thread.sleep方法。而run()方法和runnable接口并没有抛出此类异常。
子类不能抛出父类不曾有的异常的原则,因此不能用throws InterruptedException。必须用try catch来捕获此类异常。
线程类有一个方法叫interrupt()。是用来中断此线程的,比stop稍微“温柔”一点。但是同样不提倡使用。这里需要注意interrupt()方法并不是静态方法,因此需要类对象来调用。
*/
import java.util.*;
class MyThread implements Runnable
{
publicvoid run()
{
while(true)
{
System.out.println("--------"+newDate()+"-----");
try
{
Thread.sleep(1000);
}catch(InterruptedExceptione)
{
return;
}
}
}
}
public class TestSleep
{
publicstatic void main(String [] args)
{
MyThreadth=new MyThread();
Threadthread=new Thread(th);
thread.start();
try
{
Thread.sleep(10000);
} catch(InterruptedException e)
{
}
thread.interrupt();
}
}
Thread join()
/*
threadjoin()方法的使用,在runnable接口中并没有join()方法和getName()方法,runnable里面只有run()方法。如果想要使用除了
run()以外得方法,我们自己定义的线程类只能是从Thread继承过来的。只有这样的子类才有Thread的里面的方法。
join方法是这样运行的,需要加入的现成对象名.join().表示加入到当前的线程前执行。相当于方法的调用。在下面的程序中会看到先执行
完MyThread线程以后在执行Main现成,这是应为调用了thread.join()方法。由“分叉”回到一个枝。
*/
class MyThread extends Thread
{
MyThread(Strings)
{
super(s);
}
publicvoid run()
{
for(inti=1;i<=10;i++){
System.out.println("------iam:"+getName()+"-----");
try
{
Thread.sleep(1000);
}catch(InterruptedExceptione)
{
return;
}
}
}
}
public class TestJoin
{
publicstatic void main(String [] args)
{
MyThreadthread=new MyThread("abc");
thread.start();
try{
thread.join();
}catch(InterruptedExceptione)
{
}
for(inti=0;i<10;i++)
{
System.out.println("mainthread is run……");
}
}
}
Yield 静态方法,让出一下CPU。“高风亮节
class MyThread extends Thread
{
MyThread(Strings)
{
super(s);
}
publicvoid run()
{
for(inti=1;i<=100;i++)
{
System.out.println("-----"+getName()+"---"+i);
if(i%10==0)
{
Thread.yield();
System.out.println("===="+getName()+"让了");
}
}
}
}
public class TestYield
{
publicstatic void main(String [] args)
{
MyThreadth1=new MyThread("郭子");
MyThreadth2=new MyThread("段");
th1.start();
th2.start();
}
}
Priority
/*不能把代码直接写到类里面,这样会提示 需要<标识符>
关于优先级的问题setPriority(inti);
*/
class MyThread implements Runnable
{
// MyThread(Strings)
// {
// super(s);
// }
publicvoid run()
{
for(inti=0;i<100;i++)
{
System.out.println("t1"+"---"+i);
}
}
}
classMyThread2 implements Runnable
{
// MyThread(Strings)
// {
// super(s);
// }
publicvoid run()
{
for(inti=0;i<100;i++)
{
System.out.println("t2"+"-----"+i);
}
}
}
publicclass TestPriority
{
publicstatic void main(String [] args)
{
MyThreadt1=new MyThread();
MyThread2t2=new MyThread2();
Threadtt1=new Thread(t1);
Threadtt2=new Thread(t2);
tt1.setPriority(Thread.NORM_PRIORITY+3);
tt1.start();
tt2.start();
}
}
正确关闭线程的方法:
/*正确的关闭线程的方法
正确关闭线程的方法不是用 线程类的对象名.interrupet()方法,当然这样是可以停止线程的。但是太粗暴了,而是在线程类中增加一个布尔类型的标记,当这个标记是true的时候执行
当然这个标记是私有的,类的外部暴露一个共有的shutdown方法,来设置标记的true或者false。
*/
class MyThread implements Runnable
{
privateboolean flag=true;
publicvoid run()
{
inti=1;
while(true)
{
if(flag==false)
{
System.out.println("MyThreadis over");
break;
}
System.out.println("MyThreadis running "+i++);
}
}
publicvoid shutdown()
{
flag=false;
}
}
public class TestThread
{
publicstatic void main(String [] args)
{
MyThreadthread=new MyThread();
Threadth=new Thread(thread);
th.start();
for(inti=0;i<100;i++)
{
System.out.println("mainthread is running......."+(i+1));
}
System.out.println("主线程结束");
thread.shutdown();
}
}
线程同步:
/*两个线程同时访问Timer对象的add方法,当访问的时候就会出现t1现在是第num次访问
运行结果:t1第2次访问Timer
t2第2次访问Timer
分析结果:这是因为在第一个线程运行到Thread.sleep(1)的时候就停了1毫秒,这样就把执行权利交给了t2。此时的num已经++ num=1。而t2运行到Thread.sleep(1)的时候同样停了1毫秒,这样就把执行全力交给了t1。但是这个时候的num++ num=2.了。所以才会出现都是第2次访问。
*/
/*class Timer
{
privatestatic int num=0;
publicvoid add(String name)
{
num++;
try
{
Thread.sleep(1);
}catch(InterruptedExceptione)
{}
System.out.println(name+"你是第"+num+"次访问Timer");
}
}
public class TestSync implements Runnable
{
Timer timer=new Timer();
publicstatic void main(String [] args)
{
TestSyncthread=new TestSync();
Threadth1=new Thread(thread);
Threadth2=new Thread(thread);
th1.setName("t1");
th2.setName("t2");
th1.start();
th2.start();
}
publicvoid run()
{
timer.add(Thread.currentThread().getName());
}
}*/
class Timer
{
privatestatic int num=0;
publicvoid add(String name)
{
num++;
try
{
Thread.sleep(1);
}catch(InterruptedExceptione)
{}
System.out.println(name+"第"+num+"次访问Timer");
}
}
class MyThread implements Runnable
{
Timertimer=new Timer();
publicvoid run()
{
timer.add(Thread.currentThread().getName());
}
publicstatic void main(String [] args)
{
MyThreadthread=new MyThread();
Threadt1=new Thread(thread);
Threadt2=new Thread(thread);
t1.setName("guozijie");
t2.setName("duanran");
t1.start();
t2.start();
}
}
写一个“死锁”:
/*MyDeadLock.java------------死锁的演示
多线程的产生就可能会引起死锁,A线程在执行的过程中,为了防止实现线程同步,需要锁定a对象,在执行a锁内中还需要锁定b对象。a锁才能运行结束。
B线程在执行过程中,需要锁定b对象,在执行b锁还需要锁定a对象才能执行结束。而这时候a对象正在被A线程锁着呢,导致了B线程进不去,只能等着A线程用完a对象里面锁。
同样A也在等着B的把b对象的锁子用完。产生"死锁".
*/
public class MyDeadLock implements Runnable
{
publicint flag=1;
publicstatic Object o1=new Object();
publicstatic Object o2=new Object();
publicvoid run()
{
System.out.println("当前线程:"+Thread.currentThread().getName());
if(flag==1)
{
synchronized(o1)
{
System.out.println("接下来我需要o2锁子");
try
{
Thread.sleep(300);
}catch(InterruptedExceptione)
{
}
synchronized(o2)
{
System.out.println("两个锁子flag="+flag);
}
}
}
if(flag==0)
{
synchronized(o2)
{
//在这里停一下,让打断这个线程
System.out.println("接下来我需要01锁子");
try
{
Thread.sleep(300);
}catch(InterruptedExceptione)
{
}
synchronized(o1)
{
System.out.println("两个锁子flag="+flag);
}
}
}
}
publicstatic void main(String [] args)
{
MyDeadLocklock1=new MyDeadLock();
MyDeadLocklock2=new MyDeadLock();
Threadth1=new Thread(lock1);
Threadth2=new Thread(lock2);
th1.setName("t1");
th2.setName("t2");
lock1.flag=1;
lock2.flag=0;
th1.start();
th2.start();
}
}
一道面试题:
/*
在下面的程序中确实把方法2s锁定了,把它里面的的内容都锁定了,但是同样m2并没有锁定,其他的线程还是可以访问b。
因此一下输出是这是m2()方法的b=1000;
这是m1()方法的b=1000
设计一个同步的对象的时候,比较麻烦,掌握的原则的是只要需要改里面对象的值的函数就需要加上锁子。只读的对象就不需要加锁子了。
*/
public class TT implements Runnable
{
intb=100;
publicsynchronized void m1()
{
//synchronized(this){
b=1000;
try
{
Thread.sleep(5000);
}
catch(Exceptione)
{
e.printStackTrace();
}
System.out.println("这是m1方法的b="+b);
//}
}
publicvoid m2()
{
System.out.println("这是m2()方法的b="+b);
}
publicvoid run()
{
m1();
}
publicstatic void main(String [] args)
{
TTtt=new TT();
Threadt=new Thread(tt);
t.start();
/*
try
{
Thread.sleep(1000);
}catch(Exceptione)
{}*/
tt.m2();
}
}
- java多线程完全版
- 多线程-------java版
- java版多线程下载
- [置顶] java版多线程下载
- 【Java多线程】多线程死锁
- Java多线程编程总结 java 1.6版
- Java 多线程
- java 多线程
- java多线程
- JAVA多线程
- java多线程
- JAVA多线程
- java多线程
- JAVA 多线程
- Java多线程
- java多线程
- JAVA 多线程
- Java 多线程
- 奇数在前,偶数在后,可以有顺序改变
- Swift UIButton设置动态图片
- jquery删除操作option
- C/C++题目总结【来自牛客网】
- 链路层网络编程技术
- 多线程-------java版
- XCode实现注释所选行/**/功能
- 君临天下服务端架构调研
- WPF学习开发历程(三)——文本控件
- Sqlserver2008 插入中文乱码
- IOS9 SDK下载、设备安装IOS9系统
- 如何解决事务操作中状态不一致的现象?
- c#文件的读写
- 通过telnet特殊字符进行ftp攻击逃逸