java-多线程(一)

引言
主要介绍多线程的相关概念
进程和线程
线程是依赖于进程而存在的。
A:进程 正在运行的应用程序
B:线程 进程的执行路径,执行单元
多线程的两种方案:
继承Thread类(查看api简单介绍Thread类):
实现Runable接口:
有关多线程的深入理解,用一个图片来进行说明:
这里写图片描述
(3)多线程的几个问题:
A:启动线程用的是哪个方法
start()
B:start()和run()的区别
start():1.开启线程 2.执行run()方法里面的代码
run():执行的是线程里面执行的代码,并不会开启线程
C:为什么要重写run()
因为每个线程需要执行的代码都是都是不一样的,
我们需要将每个线程自己独立执行的代码写到run()方法中执行
D:线程可以多次启动吗
下面有关开启多线程的两个方法分别用相关代码做一简单的介绍:

//继承Thread类
package com.stu_01;
//继承Thread类(查看api简单介绍Thread类):
public class MyThread extends Thread{
     @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}
//测试
package com.stu_01;
public class Test {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
    }

}
//实现runable接口
package com.stu_02;
//实现Runable接口:
public class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}
//测试
package com.stu_02;
public class Test {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t = new Thread(mt);
        t.start();
    }
}

以上两个程序运行结果相同,如下:
0
1
2
3
4
5
6
7
8
9
那么,对于多线程的启动和执行过程又是如何进行的呢?
用一个图解进行理解,就用上面所用代码:
这里写图片描述
线程的调度和控制
线程休眠(Thread.sleep(毫秒值))
线程名称(setName(),getName();)
线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
什么叫线程优先级:就是设置你抢占cpu执行权抢占到的概率
多线程安全问题
A:是否是多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
* 如何解决线程安全问题??
1.存在多线程的情况
改不了
2.多个线程之间存在共享数据
改不了
3.存在多条语句操作共享数据
可以改变
关于多线程的安全问题,我想用一个生活中的例子进行说明。
卖票的问题,在这里,你可以选择继承线程或者实现接口两种方式之一,我下面的代码就用继承做一说明。再不加睡眠的情况下,卖票正常进行,但考虑到实际情况,还是需要加上睡眠相对合适,但是当加上睡眠情况后,卖票就不能正常进行,会出现卖到0和-1的情况,这显然是不对的,下面可以用简单代码演示该问题:

package com.stu_06;
public class MyThread extends Thread{
     static int ticket=100;
    @Override
    public void run() {
        while (true) {
            if (ticket>0) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(this.getName()+"正在出售第"+ticket--+"张票");
            }
        }
    }
}
//测试
package com.stu_06;
public class Test {
    public static void main(String[] args) {
        //创建线程
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        MyThread mt3 = new MyThread();
        //给线程命名
        mt1.setName("窗口一");
        mt2.setName("窗口二");
        mt3.setName("窗口三");
        //开启线程
        mt1.start();
        mt2.start();
        mt3.start();    
    }
}

编译运行部分结果:
窗口二正在出售第2张票
窗口三正在出售第1张票
窗口一正在出售第0张票
窗口二正在出售第-1张票
就如同上面所写的这样,这就会出现线程安全问题,那么该如何解决线程安全问题呢?
如何解决多线程安全问题(掌握)
如果线程安全了,线程安全执行效率就低,但是解决线程安全问题还是很有必要的。
A:同步代码块
synchronized(对象) {
需要被同步的代码。
}
1.对象是什么 ?
答:任意对象 ,相当于是一把锁,只要线程进去就把锁锁上
2.需要同步的代码?
答:被线程执行的代码
B:锁对象问题(仅适用于实现runable接口)
a:同步代码块(定义一个抽象类,里面专门定义一个锁)
任意对象
b:同步方法
public synchronized void sellTicket(){同步代码}
this
c:静态同步方法
类的字节码对象
public static synchronized void sellTicket() {
需要同步的代码
}
在这里,仅演示使用b方法解决额线程安全问题,同样是上面相同的代码,加上同步方法后,我们可以看看结果,如下:

package com.stu_08;
/*
    b:同步方法(仅适用于实现runable接口)
            public synchronized void sellTicket(){同步代码}
                this            
 */
public class MyThread implements Runnable{
    static int ticket=100;
    int x=0;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if(x%2==0){
                if (ticket>0) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket--+"张票");
                }  
                }else{
                    sellTicket();
                }   
            }
        }   
    }
    public synchronized void sellTicket(){
        if (ticket>0) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在出售第"+"张票");
        }
    }
}
//测试
package com.stu_08;
//同步代码块(定义一个抽象类,里面专门定义一个锁)
//任意对象
public class Test {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //创建进程
        Thread mt1 = new Thread(mt);
        Thread mt2 = new Thread(mt);
        Thread mt3 = new Thread(mt);
        //给进程命名
        mt1.setName("窗口一");
        mt2.setName("窗口二");
        mt3.setName("窗口三");
        //启动进程
        mt1.start();
        mt2.start();
        mt3.start();    
    }
}

编译运行后结果如下:(只显示部分结果)
窗口二正在出售第9张票
窗口三正在出售第8张票
窗口一正在出售第7张票
窗口三正在出售第6张票
窗口二正在出售第5张票
窗口三正在出售第4张票
窗口一正在出售第3张票
窗口三正在出售第2张票
窗口二正在出售第1张票
匿名内部类的方式使用多线程
new Thread() {
public void run() {

}
}.start();
new Thread(new Runnable(){
public void run() {

}
}).start();
对于匿名内部类,做一个简单的案例进行理解
案例:利用匿名内部类,启动多个线程,验证单例设计模式之懒汉式所存在的缺陷,当使用多线程来搞的时候就不单例了。

package com.stu_11;
/*
 * 案例:利用匿名内部类,启动多个线程,验证单例设计模式之懒汉式所存在的缺陷,
          当使用多线程来搞的时候就不单例了。。
 */
public class SingleInstanceStu {
     private SingleInstanceStu(){}
     private static SingleInstanceStu instance=null;
     public static SingleInstanceStu getInstance(){
         try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         if (instance==null) {
            instance=new SingleInstanceStu();
        }    
         return instance;
     }
}
//测试
package com.stu_11;
public class Test {
    public static void main(String[] args) {
        new Thread(){@Override
        public void run() {
    System.out.println(SingleInstanceStu.getInstance());
        }
    }.start();//com.stu_11.SingleInstanceStu@6c0267a
        new Thread(new Runnable() {         
            @Override
            public void run() {
    System.out.println(SingleInstanceStu.getInstance());
            }   }).start();//com.stu_11.SingleInstanceStu@41aff40f
}
}

后面注释会发现在运行结果中会出现两个不同的地址,这也正是在使用单例模式中的懒汉模式时会发生的错误,但是在使用饿汉模式时就不会出现上述情况。
JDK5的Lock锁,我们之前造的所有的锁都没有手动释放锁
lock是一个接口,它的一个实现类是ReentrantLock,因此在定义一个lock锁的时候使用如下方法:
static Lock lock = new ReentrantLock();
加锁:lock.lock();
释放锁:lock.unlock();
可以让我们明确的知道在哪里加锁和释放锁。
为了保证我们创建的锁一定会被释放,用一下代码进行改进
try{….}finally{…..}
对于lock锁,我们仍然用卖票这个例子做一说明:

package com.stu_12;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread implements Runnable{
    static int ticket=100;
    //创建一个锁
    Lock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try{
            lock.lock();
            if (ticket>0) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在售第"+ticket--+"张票");      
            }
            }
            finally{
                lock.unlock();
            }
        }   
    }
}
//测试
package com.stu_12;
public class Test {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //创建线程
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);
        //给线程命名
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        //开启线程
        t1.start();
        t2.start();
        t3.start(); 
    }
}

死锁问题
同步嵌套,锁里面套了一个锁,出现同步嵌套
线程等待和唤醒机制
锁对象调用wait()锁对象调用notify()
注意:
wait和sleep的区别
线程等待,在等待的同时释放锁,而sleep()方法在执行的过程中是不会释放锁的
对于等待唤醒机制,做一个简单的案例:

package com.stu_14;
//定义一个锁
public class MyLock {
    public static final Object obj=new Object(); 
}
//wait的代码
package com.stu_14;
public class WaitThread extends Thread{
    @Override
    public void run() {
        synchronized (MyLock.obj) {
            try {
                MyLock.obj.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("我被唤醒了");
    }
}
//唤醒代码
package com.stu_14;
public class NotifyThread extends Thread{
      @Override
    public void run() {
        synchronized (MyLock.obj) {
            MyLock.obj.notify();
        }
    }
}

编译运行代码
我被唤醒了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值