当前位置: 移动技术网 > IT编程>开发语言>Java > java多线程相关代码

java多线程相关代码

2019年02月26日  | 移动技术网IT编程  | 我要评论

1.创建线程的三种方式

  使用thread

package com.wpbxx.test;

//1.自定义一个类,继承java.lang包下的thread类
class mythread extends thread{
    //2.重写run方法
    @override
    public void run() {
        //3.将要在线程中执行的代码编写在run方法中
        for(int i = 0; i < 1000; i++) {
            system.out.println("wpb");
        }
    }
}
public class helloworld {

    public static void main(string[] args) {
        //4.创建上面自定义类的对象
        mythread mt  = new mythread();
        //5.调用start方法启动线程
        mt.start();
        for(int i = 0; i< 1000; i++) {
            system.out.println("xx");
        }
    }

}

使用runnable

package com.wpbxx.test;

//1.自定义一个类实现java.lang包下的runnable接口
class myrunnable implements runnable{
    //2.重写run方法
        @override
    public void run() {
            //3.将要在线程中执行的代码编写在run方法中
        for(int i = 0; i < 1000; i++) {
            system.out.println("wpb");
        }
    }
}
public class helloworld {

    public static void main(string[] args) {
        //4.创建上面自定义类的对象
        myrunnable mr = new myrunnable();
        //5.创建thread对象并将上面自定义类的对象作为参数传递给thread的构造方法
        thread t = new thread(mr);
        //6.调用start方法启动线程
        t.start();
        for(int i = 0; i < 1000; i++) {
            system.out.println("xx");
        }
    }

}

使用callable接口创建的线程会获得一个返回值并且可以声明异常。

优点: 可以获取返回值 可以抛出异常

线程池

线程池是初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时直接去这个线程集合中获取,而不是创建一个线程。任务执行结束后,线程回到池子中等待下一次的分配。

线程池的作用
解决创建单个线程耗费时间和资源的问题。

package com.wpbxx.test;

import java.util.concurrent.callable;
import java.util.concurrent.executionexception;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
import java.util.concurrent.future;
//1.自定义一个类实现java.util.concurrent包下的callable接口
class mycallable implements callable<integer>{
    private int count;
    public mycallable(int count) {
        this.count = count;
    }
    //2.重写call方法
        @override
    public integer call() throws exception{
            //3.将要在线程中执行的代码编写在call方法中
        for(int i = 0; i < 100; i++) {
            count++;
        }
        return count;
    }
}
public class helloworld {

    public static void main(string[] args) {
        //4.创建executorservice线程池 里面为线程的数量
        executorservice es = executors.newfixedthreadpool(2);
        ////创建一个线程池,里面的线程会根据任务数量进行添加
        //executorservice es = executors.newcachedthreadpool();
    
        //5.将自定义类的对象放入线程池里面
        future<integer> f1= es.submit(new mycallable(5));
        future<integer> f2 = es.submit(new mycallable(3));
        try {
            //6.获取线程的返回结果
            system.out.println(f1.get());
            system.out.println(f2.get());
            
        } catch (interruptedexception e) {
            // todo auto-generated catch block
            e.printstacktrace();
        } catch (executionexception e) {
            // todo auto-generated catch block
            e.printstacktrace();
        }
        //7.关闭线程池,不再接收新的线程,未执行完的线程不会被关闭
        es.shutdown();
    }

}

继承thread
  优点:可以直接使用thread类中的方法,代码简单
  缺点:继承thread类之后就不能继承其他的类
实现runnable接口
  优点:即时自定义类已经有父类了也不受影响,因为可以实现多个接口
  缺点: 在run方法内部需要获取到当前线程的thread对象后才能使用thread中的方法
实现callable接口
  优点:可以获取返回值,可以抛出异常
  缺点:代码编写较为复杂

package com.wpbxx.test;

import java.util.concurrent.callable;
import java.util.concurrent.executionexception;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
import java.util.concurrent.future;

//简易写法  使用匿名内部类创建多线程


public class helloworld {

    public static void main(string[] args) throws interruptedexception, executionexception {
        new thread() {
            public void run() {
                for(int i = 0; i < 1000; i++) {
                    system.out.println("wpb");
                }
            }
        }.start();
        
        new thread(new runnable() {
            public void run() {
                for(int i = 0; i< 1000; i++) {
                    system.out.println("xx");
                }
            }
        }).start();
        
        executorservice exec = executors.newcachedthreadpool();
        future<integer> result = exec.submit(new callable<integer>() {
            
        @override
        public integer call() throws exception{
            return 1024;
        }
        });
        
        system.out.println(result.get());
    }

}

thread设置线程的名字

方法一

new thread("马化腾") {                            //通过构造方法给name赋值
            public void run() {
                system.out.println("我是" + this.getname() + ",来腾讯工作吧 ");
            }
        }.start();

方法二

new thread() {
            public void run() {
                this.setname("马化腾");      //调用setname
                system.out.println("我是" + this.getname() + ",来腾讯啊");
            }
        }.start();

使用thread.currentthread() 获得正在运行的线程

可以这样改变runnable中线程名字

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) {
      new thread(new runnable() {
          public void run() {
          system.out.println(thread.currentthread().getname());
          thread.currentthread().setname("wpb");
          system.out.println(thread.currentthread().getname());
          }
      }).start();
    }

}

线程睡眠

thread中的sleep方法可以使当前线程睡眠,线程睡眠后,里面的任务不会执行,待睡眠时间过后会自动苏醒,从而继续执行任务。

thread.sleep(1000);   //让当前线程睡眠1秒

线程的优先级

setpriority()方法接收一个int类型的参数,通过这个参数可以指定线程的优先级,取值范围是整数1~10,优先级随着数字的增大而增强。  但并不是一定执行优先级高的执行完之后  才执行别的

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) {
        thread t1 = new thread() {
            public void run() {
                for(int i = 0; i<100; i++) {
                    system.out.println("wpb");
                }
            }
        };
        
        thread t2 = new thread() {
            public void run() {
                for(int i = 0; i < 100; i++) {
                    system.out.println("1024");
                }
            }
        };
        
        t1.setpriority(10);
        t2.setpriority(0);
        t1.start();
        t2.start();
        
        
    }

}

唤醒睡眠中的线程

  t1.interrupt();

用interrupt方法会抛出一个interruptedexception的异常。

 

同步方法

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) {
    
        task tk = new task();
        
        thread t1 = new thread() {
            public void run() {
                tk.changenum(true);
            }
        };
        thread t2 = new thread() {
            public void run() {
                tk.changenum(false);
            }
        };
        t1.start();
        t2.start();
    }

}
class task{
    
    private int num = 0;
    public void changenum(boolean flag) {
        if(flag) {
            num = 99;
            system.out.println(thread.currentthread().getname() + "-------" + "begin");
            system.out.println(thread.currentthread().getname() + "-------" + num);
            system.out.println(thread.currentthread().getname() + "-------" + "end");
        }else {
            num = 22;
            system.out.println(thread.currentthread().getname() + "-------" + "begin");
            system.out.println(thread.currentthread().getname() + "-------" + num);
            system.out.println(thread.currentthread().getname() + "-------" + "end");
        }
    }
}

正常情况下应该打印出一个88一个66,可是上面却两个线程打印出的两个66,这样就出现了线程安全的问题,出现这个问题的原因是成员变量存储在堆内存中,两个线程共享堆内存,即两个线程可以对同一个num进行修改。
程序执行分析:
cpu执行t1线程,将num修改为88,之后cpu开始执行t2线程,将num修改为66,打印出66,cpu开始执行t1线程,打印num的值,此时num的值是66。

 

在方法上加入synchronized关键字,这样在执行多个线程时看哪个线程先执行这个方法,假设有t1,t2,t3三个线程中都调用了changenum方法,t1线程先执行了这个方法,那么t1会先在task对象上面加锁,加锁后,别的线程就无法执行当前task对象上的changenum方法,直到t1执行结束changenum方法之后,t2,t3中的一个线程才可以执行这个方法,这就保证了在某个时间段内只有一个线程执行changenum方法,解决了线程安全问题。
注意:synchronized锁住的是当前对象,如果t1线程和t2线程里面是不同的对象,则不需要同步,因为不会发生线程安全问题

 

public synchronized void changenum(boolean flag)  加这一句就ok了


也可以对需要互斥访问的代码块加上synchronized
    public void changenum(boolean flag){

        try {
            thread.sleep(3000);
            system.out.println("执行一个耗时较长的任务");
        } catch (interruptedexception e) {
            e.printstacktrace();
        }

        //这个方法中,需要同步的代码块是这部分,而上面耗时操作的代码,不涉及到线程安全问题,所以不需要同步
        synchronized(obj){
            if(flag){
                num = 88;

                system.out.println(thread.currentthread().getname() + "========== begin");
                system.out.println(thread.currentthread().getname() + "==========" + num);
                system.out.println(thread.currentthread().getname() + "========== end");
            }else{
                num = 66;

                system.out.println(thread.currentthread().getname() + "========== begin");
                system.out.println(thread.currentthread().getname() + "==========" + num);
                system.out.println(thread.currentthread().getname() + "========== end");
            }
        }

    }

死锁

发生死锁原因就是两个或多个线程都在等待对方释放锁导致,下面通过代码来演示一下死锁情况。

package com.wpbxx.test;

public class helloworld {

    private static object obj1 = new object();
    private static object obj2 = new object();
    public static void main(string[] args) {
        new thread() {
            public void run() {
                synchronized(obj1) {
                    system.out.println(this.getname());
                    
                    synchronized(obj2) {
                        system.out.println(this.getname());
                    }
                }
            }
        }.start();
         new thread() {
            public void run() {
                synchronized(obj2) {
                    system.out.println(this.getname());
                    
                    synchronized(obj1) {
                        system.out.println(this.getname());
                    }
                }
            }
        }.start();
    }
}

 

volatile关键字

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) throws interruptedexception {
        
        task task = new task();
        
        thread t1 = new thread(task);
        
        t1.start();
        
        thread.sleep(100);
        task.setflag(false);
    }
}
class task implements runnable{
    
    private boolean flag = true;
    
    public boolean isflag() {
        return flag;
    }
    
    public void setflag(boolean flag) {
        this.flag = flag;
    }
    
    public void run() {
        while(flag) {
            system.out.println("while循环");
        }
        system.out.println("循环结束");
    }
}

上面程序中在64位的机器上以server模式运行时,有可能会出现死循环的现象。

jvm的运行可以分为下面两种模式:

  • client:启动快,运行后性能不如server模式,一般运行时默认是client模式
  • server:启动慢,运行后性能比client模式好。

在eclipse中可以通过配置来使用server模式,右键—>run as—>run configurations。写上-server。然后点击run即可

上面程序出现问题的原因这样的,虽然在主线程中将flag的设置为false,但是jvm为了提升效率,t1线程一直在私有内存中获取flag的值,而私有内存中的flag值并没有被改变,所以导致死循环的发生。

使用volatile修饰flag解决上面问题:

volatile private boolean flag = true;

将flag声明为volatile后,t1线程会从公共的内存中访问flag的值,这样在主线程将flag设置为false后,t1线程中的循环就会结束了。

注意:volatile只能修饰变量,不能修饰方法

原子性和非原子性

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

非原子性:不符合原子性的就是非原子性

  int x = 1024; //原子性

    int y = x; //cpu先去内存中读取x的值,读取后在为y进行赋值,在读取后给y赋值前的这段时间可能会切换到其他线程上面。

    x++; //包含了三个操作,先读取x的值,然后进行加1操作,最后写入新的值,在这三个操作的间隙可能会切换到其他线程上面。

    x = x + 1; //同上

volatile是非原子性的。
synchronized是原子性的。

 timertask

timertask是一个实现了runnable接口的抽象类,需要编写一个类继承timertask类,将要在定时任务执行的代码编写在run方法中。

要想执行定时任务,需要创建timer的对象并调用里面的schedule方法,在timer类中有多个重载的schedule方法,这里咱们使用这个:

schedule(timertask task, date firsttime, long period);

package com.wpbxx.test;

import java.text.parseexception;
import java.text.simpledateformat;
import java.util.timer;
import java.util.timertask;

public class helloworld {

    public static void main(string[] args) throws interruptedexception, parseexception {
        
        timer t = new timer();
        t.schedule(new mytimertask(), new simpledateformat("yyyy-mm-dd hh:mm:ss sss").parse("2017-07-03 18:09:00 000"),
                5000);
    }
}
class mytimertask extends timertask{
    @override
    public void run() {
        system.out.println("wpbxx");
    }
}

 

 

线程之间的通信

多线程环境下cpu会随机的在线程之间进行切换,如果想让两个线程有规律的去执行,那就需要两个线程之间进行通信,在object类中的两个方法wait和notify可以实现通信。

wait方法可以使当前线程进入到等待状态,在没有被唤醒的情况下,线程会一直保持等待状态。
notify方法可以随机唤醒单个在等待状态下的线程

 

利用wait  和notify  进行交替打印

package com.wpbxx.test;

import java.text.parseexception;
import java.text.simpledateformat;
import java.util.timer;
import java.util.timertask;

public class helloworld {

    public static void main(string[] args) throws interruptedexception, parseexception {
        print p = new print();
        thread t1 = new thread() {
            public void run() {
                while(true) {
                    p.print1();
                }
            }
        };
        thread t2 = new thread() {
            public void run() {
                while(true) {
                    p.print2();
                }
            }
        };
        t1.start();
        t2.start();
    }
}
class print{
    private int flag = 1;
    
    public void print1() {
        synchronized(this) {
            if(flag != 1) {
                try {
                    this.wait();
                }catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
            
            system.out.println("wpb");
            flag = 2;
            this.notify();
        }
    }
    public void print2() {
        synchronized(this) {
            if(flag != 2) {
                try {
                    this.wait();
                }catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
            
            system.out.println("xx");
            flag = 1;
            this.notify();
        }
    }
}

 

但这样如果是三个线程以上的  就不行,  可能出现死锁了

因为是随机唤醒一个等待的线程,  假设一线程 进行玩后 随即唤醒一个线程,并把flag = 2,  但这时唤醒了线程3  就会一直等待

notifyall()  为唤醒所有的线程

package com.wpbxx.test;

/**
 * 三个(三个以上)线程之间的通信
 *
 */
public class helloworld {

    public static void main(string[] args) {

        print1 p = new print1();

        thread t1 = new thread(){
            public void run(){
                while(true){
                    p.print1();
                }

            }
        };

        thread t2 = new thread(){
            public void run(){
                while(true){
                    p.print2();
                }
            }
        };

        thread t3 = new thread(){
            public void run(){
                while(true){
                    p.print3();
                }
            }
        };

        t1.start();
        t2.start();
        t3.start();
    }

}

class print1{

    private int flag = 1;

    public void print1(){
        synchronized(this){
            while(flag != 1){
                try {
                    //让当前线程进入等待状态
                    this.wait();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }

            system.out.println("monkey");
            flag = 2;
            //唤醒所有等待的线程
            this.notifyall();
        }

    }

    public void print2(){
        synchronized(this){
            while(flag != 2){
                try {
                    this.wait();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }

            system.out.println("1024");
            flag = 3;
            this.notifyall();
        }

    }

    public void print3(){
        synchronized(this){
            while(flag != 3){
                try {
                    this.wait();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }

            system.out.println("888");
            flag = 1;
            this.notifyall();
        }

    }
}
view code

这样就可以实现三个线程的交替打印, 但会有问题  就是唤醒所有的线程  开销太大。

 

 

上面notify() 或者 notifyall()  并不能唤醒指定的线程,所以多出了  互斥锁

新增了  reentrantlock类 和    condition接口  来替换   synchronized关键字   和   wait、notify  方法。

reentrantlock类     和condition接口    都在java.util.concurrent.locks包下。
可以使用       reentrantlock类中    的  lock方法   和   unlock方法     进行上锁和解锁,用来替代synchronized关键字。
condition接口中的await方法和signal方法用来让线程等待和唤醒指定线程。用来替代wait方法和notify方法。

 

如 还是循环打印东西

package com.wpbxx.test;

import java.util.concurrent.locks.condition;
import java.util.concurrent.locks.reentrantlock;

public class helloworld {

    public static void main(string[] args) throws interruptedexception {
        
        print p = new print();
        thread t1 = new thread() {
            public void run() {
                while(true) {
                    p.print1();
                }
            }
        };
        thread t2 = new thread() {
            public void run() {
                while(true) {
                    p.print2();
                }
            }
        };
        thread t3 = new thread() {
            public void run() {
                while(true) {
                    p.print3();
                }
            }
        };
        t1.start();
        t2.start();
        t3.start();
    }
}
class print{
    private reentrantlock r = new reentrantlock();
    
    private condition c1 = r.newcondition();
    private condition c2 = r.newcondition();
    private condition c3 = r.newcondition();
    
    private int flag = 1;
    public void print1() {
        r.lock();
        
        while(flag != 1) {
            try {
                c1.await();
            } catch (interruptedexception e) {
                // todo auto-generated catch block
                e.printstacktrace();
            }
        }
        system.out.println("wpb1");
        flag = 2;
        c2.signal();
        r.unlock();
    }
    
    public void print2() {
        r.lock();
        
        while(flag != 2) {
            try {
                c2.await();
            } catch (interruptedexception e) {
                // todo auto-generated catch block
                e.printstacktrace();
            }
        }
        system.out.println("wpb2");
        flag = 3;
        c3.signal();
        r.unlock();
    }
    public void print3() {
        r.lock();
        
        while(flag != 3) {
            try {
                c3.await();
            } catch (interruptedexception e) {
                // todo auto-generated catch block
                e.printstacktrace();
            }
        }
        system.out.println("wpb3");
        flag = 1;
        c1.signal();
        r.unlock();
    }
    
    
}

以后再补充

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网