-------android培训、java培训、期待与您交流! ----------
多线程
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序
该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行
一个进程中至少有一个线程
二.Java中的线程:
Java VM启动的时候会有一个进程java.exe
该进程中至少有一个线程负责java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
Java使用thread类代表线程,所有线程对象都必须是Thread类或其子类的实例
Thread类用于描述线程
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法
也就是说Thread中的run方法,用于存储线程要运行的代码。
为什么要把代码放进main方法中:
因为java虚拟机的主线程需要调用main方法中的代码
三.自定义线程。
方式一:直接继承Thread类。并覆盖run方法
调用线程的start方法:
该方法两个作用:启动线程,调用run方法
方式二:实现Runnable接口。再把实现类传递给Thread类。在构造函数中赋值。
示例代码:
package cn.itcast.heima;
public class TraditionalThread {
public static void main(String[] args) {
//第一种创建线程的方式
Thread thread1 = new Thread(){
public void run(){
while(true){
System.out.println("1 "+Thread.currentThread().getName());
}
}
};
thread1.start();
//第二种创建线程的方式
Thread thread2 =new Thread(new Runnable(){
public void run(){
while(true){
System.out.println("2 "+Thread.currentThread().getName());
}
}
});
thread2.start();
new Thread(new Runnable(){
public void run(){
while(true){
System.out.println("runable"+Thread.currentThread().getName());
}
}
}){
public void run(){
while(true){
System.out.println("thread"+Thread.currentThread().getName());
}
}
}.start();
}
}
线程的四种状态:
sleep方法需要指定睡眠时间,单位是毫秒。 一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源。
wait(),sleep()有什么区别?
Wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
多线程的安全问题:
导致安全问题的出现的原因:多个线程访问出现延迟
线程随机性
。 注:线程安全问题在理想状态下,不容易出 现,但一旦出现对软件的影响是非常大 的。
同步代码块:
同步的前提:必要有两个或者两个以上的线程
必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程都需要判断锁,较为消耗资源。
如何找问题:
1.明确哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的
同步函数:
同步函数用得是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。那就是this
所以同步函数使用的锁是this
通过一个小程序来验证:同步函数使用的锁是this
使用两个线程来买票。
一个线程在同步代码块中。
一个线程在同步函数中。
都在执行买票动作。
package Thread;
public class ThirdThread implements Runnable{
private int ticket =100;
Object obj = new Object();
boolean flag=true;
@Override
public void run() {
if(flag){
while(true){
synchronized(obj){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"....code......"+ticket--);
}
}
}
}
else{
while(true)
show();
}
}
public synchronized void show(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+".......show........."+ticket--);
}
}
public static void main(String[] args){
ThirdThread tt = new ThirdThread();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.start(); //
tt.flag=false;
t2.start();
}
}
通过这个买票的程序我们发现输出结果都是当前运行的线程名字加.....show.....加票的数量.
这是为什么呢? 当我们创建两个t1和t2线程时,程序中总的有几个线程? 有3个线程:分别是主线程和t1和t2线程。
main方法中的代码块是由主线程执行的,当主线程执行到这的时候,t1马上就运行了吗? 没有它只是获得了执行权。然后主线程继续往下执行
flag变为false了。当cpu轮换到t1和t2时,因为它们的flag都是flase所以它们执行的都是else后面的代码。
既然这样,那我们就来变变代码,在t1.start();这个代码块后面加上一句Thread.sleep(20); 这句代码块的意思是让主线程停止20毫秒。
那么t1就能执行到同步代码块这里面的代码了。我们再来运行一下程序看看,我们会发现最后尽然出现一张0号票? 这说明我们程序有问题。
那是什么问题呢? 要同步的前提是什么? 必须要有两个线程或两个以上线程在执行,锁必须是一样的。 根据这两个前提我们看看第一个条件
我们是满足的,那第二个条件呢? 前面我们说到非静态同步函数的锁是this 而我们同步代码块我们给的锁是什么? 是object 所以锁不一样
我们只要在同步代码块那里把obj改成this,再次运行代码我们就会发现没有0号票了!
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中不能定义this
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码
文件对象。类名.class 该对象的类型是Class
线程间通讯:
其实就是多个线程在操作同一个资源
但是操作的动作不同。
线程取得控制权的方法有三:
执行对象的某个同步实例方法。
执行对象对应类的同步静态方法。
执行对该对象加同步锁的同步块。
静态的同步方法,使用的锁是该方法所在类的字节码文件对象. 即:类名.class
wait;notify;notifyAll;
都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中的线程时,都必须要标识它们所操作线程持有的锁。
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义在object中
生产者与消费者实例:package itcast;
/**
* 生产者和消费者实例
* @author kang
*
*/
//生产者
class Producer implements Runnable{
private Resource r ;
public Producer(Resource res){
this.r=res;
}
@Override
public void run() {
while(true)
r.set("商品");
}
}
//资源
class Resource{
private String name;
private int count=1;
private boolean flag = false;
public synchronized void set(String name){
while(flag){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name+"----"+count++;
System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
flag =true;
this.notifyAll();
}
public synchronized void out(){
while(!flag)
{
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"---消费者---------"+this.name);
flag=false;
this.notifyAll();
}
}
//消费者
class Consumer implements Runnable{
private Resource res ;
public Consumer(Resource r){
this.res=r;
}
@Override
public void run() {
while(true)
res.out();
}
}
public class Produce{
public static void main(String[] args){
Resource rs = new Resource();
Producer p = new Producer(rs);
Consumer c = new Consumer(rs);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3= new Thread(p);
Thread t4 = new Thread(c);
Thread t5 = new Thread(c);
Thread t6 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
如何停止线程?
只有一种,那就是让run方法结束
开始多线程运行,运行代码通常是循环结构
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态,
就不会读取标记,那么线程就不会结束
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对
冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束
Thread类提供该方法interrupt();
示例代码:package itcast;
class StopThreadDemo {
public static void main(String[] args){
int count=0;
StopThread st = new StopThread();
Thread t = new Thread(st);
Thread t2 = new Thread(st);
t.start();
t2.start();
while(true){
System.out.println(Thread.currentThread().getName()+"-------"+count);
if(count++==60){
// st.changeFlag();
t.interrupt();
t2.interrupt();
break;
}
}
System.out.println("over");
}
}
class StopThread implements Runnable{
private boolean flag =true;
public synchronized void run(){
while(flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"........Exception");
flag = false;
}
System.out.println(Thread.currentThread().getName()+"..........run");
}
}
}
守护线程:
setDaemon:
该方法的作用为当正在运行的线程都是守护线程时,java虚拟机退出。守护线程也会停止运行。
该方法必须在启动线程前调用
join方法:
当A线程执行到了B线程的join方法时,A线程就会等待。等B线程都执行完,A才会执行。
jion方法可以用来临时加入线程执行。
懒汉式如果在函数上加了锁就会比较低效。
换成同步代码块效率会高一点。
Public static Single getInstance()
{
If(s==null)
}
懒汉式延迟加载。如果多线程访问时会有安全问题。用同步函数和同步代码块都可以解决安全问题。但是用同步函数效率有点低。用同步代码块加双重判断可以让效率更高点。同步代码块使用的锁事该类所属的字节码对象。