java多线程代码_java多线程相关代码

1.创建线程的三种方式

使用Thread

packagecom.wpbxx.test;//1.自定义一个类,继承java.lang包下的Thread类

class MyThread extendsThread{//2.重写run方法

@Overridepublic voidrun() {//3.将要在线程中执行的代码编写在run方法中

for(int i = 0; i < 1000; i++) {

System.out.println("wpb");

}

}

}public classhelloworld {public static voidmain(String[] args) {//4.创建上面自定义类的对象

MyThread mt = newMyThread();//5.调用start方法启动线程

mt.start();for(int i = 0; i< 1000; i++) {

System.out.println("xx");

}

}

}

使用Runnable

packagecom.wpbxx.test;//1.自定义一个类实现java.lang包下的Runnable接口

class MyRunnable implementsRunnable{//2.重写run方法

@Overridepublic voidrun() {//3.将要在线程中执行的代码编写在run方法中

for(int i = 0; i < 1000; i++) {

System.out.println("wpb");

}

}

}public classhelloworld {public static voidmain(String[] args) {//4.创建上面自定义类的对象

MyRunnable mr = newMyRunnable();//5.创建Thread对象并将上面自定义类的对象作为参数传递给Thread的构造方法

Thread t = newThread(mr);//6.调用start方法启动线程

t.start();for(int i = 0; i < 1000; i++) {

System.out.println("xx");

}

}

}

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

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

线程池

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

线程池的作用

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

packagecom.wpbxx.test;importjava.util.concurrent.Callable;importjava.util.concurrent.ExecutionException;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Future;//1.自定义一个类实现java.util.concurrent包下的Callable接口

class MyCallable implements Callable{private intcount;public MyCallable(intcount) {this.count =count;

}//2.重写call方法

@Overridepublic Integer call() throwsException{//3.将要在线程中执行的代码编写在call方法中

for(int i = 0; i < 100; i++) {

count++;

}returncount;

}

}public classhelloworld {public static voidmain(String[] args) {//4.创建ExecutorService线程池 里面为线程的数量

ExecutorService es = Executors.newFixedThreadPool(2);创建一个线程池,里面的线程会根据任务数量进行添加//ExecutorService es = Executors.newCachedThreadPool();//5.将自定义类的对象放入线程池里面

Future f1= es.submit(new MyCallable(5));

Future 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接口

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

缺点:代码编写较为复杂

packagecom.wpbxx.test;importjava.util.concurrent.Callable;importjava.util.concurrent.ExecutionException;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Future;//简易写法 使用匿名内部类创建多线程

public classhelloworld {public static void main(String[] args) throwsInterruptedException, ExecutionException {newThread() {public voidrun() {for(int i = 0; i < 1000; i++) {

System.out.println("wpb");

}

}

}.start();new Thread(newRunnable() {public voidrun() {for(int i = 0; i< 1000; i++) {

System.out.println("xx");

}

}

}).start();

ExecutorService exec=Executors.newCachedThreadPool();

Future result = exec.submit(new Callable() {

@Overridepublic Integer call() throwsException{return 1024;

}

});

System.out.println(result.get());

}

}

Thread设置线程的名字

方法一

new Thread("马化腾") { //通过构造方法给name赋值

public voidrun() {

System.out.println("我是" + this.getName() + ",来腾讯工作吧 ");

}

}.start();

方法二

newThread() {public voidrun() {this.setName("马化腾"); //调用setName

System.out.println("我是" + this.getName() + ",来腾讯啊");

}

}.start();

使用Thread.currentThread() 获得正在运行的线程

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

packagecom.wpbxx.test;public classhelloworld {public static voidmain(String[] args) {new Thread(newRunnable() {public voidrun() {

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,优先级随着数字的增大而增强。  但并不是一定执行优先级高的执行完之后  才执行别的

packagecom.wpbxx.test;public classhelloworld {public static voidmain(String[] args) {

Thread t1= newThread() {public voidrun() {for(int i = 0; i<100; i++) {

System.out.println("wpb");

}

}

};

Thread t2= newThread() {public voidrun() {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的异常。

同步方法

packagecom.wpbxx.test;public classhelloworld {public static voidmain(String[] args) {

Task tk= newTask();

Thread t1= newThread() {public voidrun() {

tk.changeNum(true);

}

};

Thread t2= newThread() {public voidrun() {

tk.changeNum(false);

}

};

t1.start();

t2.start();

}

}classTask{private int num = 0;public void changeNum(booleanflag) {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(booleanflag){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");

}

}

}

死锁

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

packagecom.wpbxx.test;public classhelloworld {private static Object obj1 = newObject();private static Object obj2 = newObject();public static voidmain(String[] args) {newThread() {public voidrun() {synchronized(obj1) {

System.out.println(this.getName());synchronized(obj2) {

System.out.println(this.getName());

}

}

}

}.start();newThread() {public voidrun() {synchronized(obj2) {

System.out.println(this.getName());synchronized(obj1) {

System.out.println(this.getName());

}

}

}

}.start();

}

}

volatile关键字

packagecom.wpbxx.test;public classhelloworld {public static void main(String[] args) throwsInterruptedException {

Task task= newTask();

Thread t1= newThread(task);

t1.start();

Thread.sleep(100);

task.setFlag(false);

}

}class Task implementsRunnable{private boolean flag = true;public booleanisFlag() {returnflag;

}public void setFlag(booleanflag) {this.flag =flag;

}public voidrun() {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);

packagecom.wpbxx.test;importjava.text.ParseException;importjava.text.SimpleDateFormat;importjava.util.Timer;importjava.util.TimerTask;public classhelloworld {public static void main(String[] args) throwsInterruptedException, ParseException {

Timer t= newTimer();

t.schedule(new MyTimerTask(), new SimpleDateFormat("yyyy-MM-dd hh:mm:ss SSS").parse("2017-07-03 18:09:00 000"),5000);

}

}class MyTimerTask extendsTimerTask{

@Overridepublic voidrun() {

System.out.println("wpbxx");

}

}

线程之间的通信

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

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

notify方法可以随机唤醒单个在等待状态下的线程。

利用wait  和notify  进行交替打印

packagecom.wpbxx.test;importjava.text.ParseException;importjava.text.SimpleDateFormat;importjava.util.Timer;importjava.util.TimerTask;public classhelloworld {public static void main(String[] args) throwsInterruptedException, ParseException {

Print p= newPrint();

Thread t1= newThread() {public voidrun() {while(true) {

p.print1();

}

}

};

Thread t2= newThread() {public voidrun() {while(true) {

p.print2();

}

}

};

t1.start();

t2.start();

}

}classPrint{private int flag = 1;public voidprint1() {synchronized(this) {if(flag != 1) {try{this.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("wpb");

flag= 2;this.notify();

}

}public voidprint2() {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()  为唤醒所有的线程

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.wpbxx.test;/*** 三个(三个以上)线程之间的通信

**/

public classhelloworld {public static voidmain(String[] args) {

Print1 p= newPrint1();

Thread t1= newThread(){public voidrun(){while(true){

p.print1();

}

}

};

Thread t2= newThread(){public voidrun(){while(true){

p.print2();

}

}

};

Thread t3= newThread(){public voidrun(){while(true){

p.print3();

}

}

};

t1.start();

t2.start();

t3.start();

}

}classPrint1{private int flag = 1;public voidprint1(){synchronized(this){while(flag != 1){try{//让当前线程进入等待状态

this.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("monkey");

flag= 2;//唤醒所有等待的线程

this.notifyAll();

}

}public voidprint2(){synchronized(this){while(flag != 2){try{this.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("1024");

flag= 3;this.notifyAll();

}

}public voidprint3(){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方法。

如 还是循环打印东西

packagecom.wpbxx.test;importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.ReentrantLock;public classhelloworld {public static void main(String[] args) throwsInterruptedException {

Print p= newPrint();

Thread t1= newThread() {public voidrun() {while(true) {

p.print1();

}

}

};

Thread t2= newThread() {public voidrun() {while(true) {

p.print2();

}

}

};

Thread t3= newThread() {public voidrun() {while(true) {

p.print3();

}

}

};

t1.start();

t2.start();

t3.start();

}

}classPrint{private ReentrantLock r = newReentrantLock();private Condition c1 =r.newCondition();private Condition c2 =r.newCondition();private Condition c3 =r.newCondition();private int flag = 1;public voidprint1() {

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 voidprint2() {

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 voidprint3() {

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();

}

}

以后再补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值