----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------...
多线程技术的了解和应用.
多线程
1,进程;什么叫做进程呢?
这个就是正在进行中的程序.进程是可以同时开启的CPU在对他们同时执行.
在我们看上去好像是同时在执行的但是其实不是的,这是CPU在做这快速的切换
只是我们的肉眼是看不见的而已.才导致我们在看上去好像是同时在执行的一样
在一个进程当中有可能出现多条执行的路劲,如迅雷.它是一个进程,但是他在下载的
时候,里面有好多的执行路劲在不同的下载,如果在下载的时候,只是一个线程在操作的
的话,在向服务端发送下载请求的时候,也可以的,一个一个下,但是呢那样的效率太
慢了.如果是多个执行路劲一起发送的话,那么将会快的很多,就好像我们在般东西一样
的,一个人般肯定是能干完的,但是浪费时候,那么可以多找点人来帮忙.那么在下载的
的时候每一个执行的路劲就是一个线程,线程是进程中的内容.进程就是用来表示空间的
当每个进程在执行的时候,在内存中都会开辟一片空间,那个空间的表示符就是进程,用于
封装里面的控制单元,
每一个进程执行都有一个执行顺序.该顺序就是一个执行路劲,或者叫一个控制单元.
2,线程;就是进程中的一个独立的控制单元,线程在控制着进程的执行.
一个进程中至少有一个线程,就是一个控制单元.
3,多线程存在的意义.
那么我们就自己定义一个线程(控制单元)来控制,执行的代码
1,如何在一个自定的代码中自定义一个线程呢?
通过对API的查找,Java已经提供了对线程这类事物的描述,就是Thread类
创建线程的第一种方式就是,继承Thread类.
按照要求写出了继承,覆盖了里面的方法.
步骤;
1,定义一个类继承Thread类
2,复写里面的run()方法
3,调用线程的start方法,有两个作用
A;启动线程
B;调用run()方法
class Demo extends Thread
{
public void run()
{
for(int x = 0;x<60x++)
{
System.out.println("demo run");
}
}
}
class Demo1
{
public static void main(String[] args)
{
Demo d = new Demo();//这就是在创建一个线程
d.start();//开始执行线程.并执行run方法.
for(int x = 0;x<60x++)
{
System.out.println("hello world");
}
}
}
在执行的时候CPU在做这快速的切换.执行一会主线程,执行一会d线程,他们之间在做这切换的动作.
形象的叫做抢劫CPU的执行权,谁抢到执行谁
那么为什么要覆盖Thread类中的run();方法呢?
该类定义了一个功能,用于存储线程要执行的代码,该存储的位置就是run方法,因为执行的内容是
不相同的所以要覆盖里面的run方法.你想执行什么代码就写什么代码.主线程的代码都存放在main
方法中
在线程中如何,获取到当前线程.用到的方法就是currentThead();
这个方法是静态的是可以通过类名直接调用的,
那么在给线程起名字的时候那么我们可以不用系统原来默认的名字那么我们可以给它重新定义名字;
可以通过setName();方法来设置,也可以通过父类的构造方法来赋值.这两种方式取其一即可;
//这是一个测试类
public class ThreadDemo {
public static void main(String[] args){
DemoThread dt = new DemoThread("zhangsan");
dt.setName("lisi");
dt.start();
for(int y = 0; y< 30;y++){
System.out.println("main........"+y);
}
}
}
/**
*
* @author Administrator
*
*/
//这是一个继承 Thread 的类
class DemoThread extends Thread{
DemoThread(String name){
super(name);
}
public void run(){
for(int x = 0; x< 30; x++){
System.out.println(this.getName()+"....."+x);
System.out.println(Thread.currentThread().getName()+"....."+x);
//上面的这两句话在表示上面的是一样的,下面那种方式是通用的.
}
}
}
只要是new出来的新线程的话,那么他们中都有一个run方法,他们是相互独立的线程.
事例演示;卖票的小示例
/*
* 这是一个简单的售票的示例,现在有10张票让三个窗口来卖票是这样的
*
* 下面的代码是什么意思呢?
* 现在有10票在下面开启的线程呢,每个线程里面都有一个run方法,那么他们在卖的时候
* 都会卖出去10张票,那么就会卖出去40张的票,那么这个就是错误的了,
* 我想让他们卖的只是这10张.这样就是不符合条件的.
* */
class Demo1 extends Thread {
int key = 10;
public void run(){
while(key > 0){
System.out.println(Thread.currentThread().getName()+".."+(key--));
}
}
}
public class Demo{
public static void main(String[] args){
Demo1 d = new Demo1();
Demo1 d1 = new Demo1();
Demo1 d2 = new Demo1();
d.start();
d1.start();
d2.start();
}
}
那么上面的问题要怎么解决呢?
那么这个时候,就想到了用静态来做,是可以的,但是呢不建议使用,因为静态的生命周期太长了.
那么就想到了,创建线程的另外一种方式就是实现 Runnable 接口,
class Demo2 implements Runnable{
int key = 20;
public void run(){
while(key > 0){
System.out.println(Thread.currentThread().getName()+"..."+key--);
}
}
}
public class Demo{
public static void main(String[] args){
Demo2 d = new Demo2();//还没有线程
Thread t = new Thread(d);
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t.start();
t1.start();
t2.start();
}
}
只有是创建Thread 对象或者是创建Thread 的子类对象才是创建了线程,这里面并没有和Thread 有任何的
关系所以在Demo2 d = new Demo2();并没有其他线程,
那么在这个时候呢我们就用两种方式创建了线程,那么第一种方式是,继承了Thread 类第二种方式,就是实现
了Runnable
那么在这个时候我们就会发现有一点小问题了 什么样的小问题的,就是线程安全的问题
那么我们在上面做得卖票的程序会不会出现线程安全问题呢?
经过我们的分析发现,是可能出现的.分析结果是这样子的.
1.当我们在执行run方法的时候,当线程1正要向往外面出票的时候卡住了,CPU执行其他的程序了,
2,这个时候线程2也进来了,也卡在这里了,当线程1醒来的时候执行卖票了,这个时候卖到0号票的时候
那么这个时候2线程也醒来了,那么这个时候呢,就不会再上去判断条件是否是满足的,那么它还会继续的
往外卖票,那么这个时候就会出现负号的票.
那么这个时候就出现了线程安全的问题?
我们怎么来演示这个线程卡住的这个,那么就是让线程在即将售票的时候
class Demo2 implements Runnable{
int key = 20;
public void run(){
while(key > 0){
try{Thread.sleep(10);)catch(Exception e){}
在这个地方为什么只能,处理而不能抛出去呢?
因为实现的是Runnable 接口,接口不存在异常所以子类也不能抛异常
System.out.println(Thread.currentThread().getName()+"..."+key--);
}
}
}
public class Demo{
public static void main(String[] args){
Demo2 d = new Demo2();//还没有线程
Thread t = new Thread(d);
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t.start();
t1.start();
t2.start();
}
}
那么在执行上面这段代码的时候就会出现线程安全的问题
为什么会产生问题呢?
当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没有执行完,
另外一个线程参与进来执行,导致共享数据的错误.
那么我们怎么来解决这个问题的呢?
对多条操作共享数据的语句,只能让一个线程都执行完,在执行的过程中,其他线程不可以参与执行
Java 对于多线程的安全问题提供了专业的解决方案.
就是同步代码块.
synchronized(对象)
{
需要被同步的代码.
}
那么同步代码块怎么用呢?
就看哪些数据在操作共享的数据.
同步代码块中的对象可以是任意的,这个对象我么可以形象的理解为锁,当线程在执行到同步代码块的时候
那么它先做的事情是判断一下锁是开的还是关的,一看是开的就进来了,那么在进来的同时呢还没有去做任何
事情先把锁先锁上在说防止别的线程也进来,那么这个时候锁就锁上了,当其他线程在过来执行到同步代码块
的时候一看锁是锁上的里面有线程我先就不进去了,在外面等着呢,只到里面的线程在执行完出来了,那么它
才进去,那么这个时候的话,操作共享数据的每次就只有一条线程在操作.
同步的条件是什么呢?
1,在同步的时候要有两个以上的线程.如果就是一个线程的话那就没有必要在使用同步了
2,用的必须是同一把锁,如果不是同一把锁的话,那么也就没有意义了,两个人在不同的卫生间那就不需要同
一把锁来完成.
必须要保证同步当中只有一个线程在执行.
同步代码块的好处就是解决了线程的安全问题.
它的弊端就是比较的消耗资源.就是每一次都要去判断一下锁每一次都要,那么这样的话效率会相对于来说的
话是比较慢的.
package cn.itcast.functionDemo;
public class Demo {
public static void main(String[] args){
RunnableTest rt = new RunnableTest();
Thread t = new Thread(rt);
Thread t1 = new Thread(rt);
Thread t2 = new Thread(rt);
t.start();
t1.start();
t2.start();
}
}
class RunnableTest implements Runnable{
private int key =100;
Object obj = new Object();
public void run(){
while(true){
synchronized (obj) {
if(key> 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
System.out.println(Thread.currentThread().getName
()+"..."+key--);
}
}
}
}
}
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------...