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

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

上面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();
} }

以后再补充

java多线程相关代码的更多相关文章

  1. Java多线程相关的

    很多小伙伴在学习Java的时候,总是感觉Java多线程在实际的业务中很少使用,以至于不会花太多的时间去学习,技术债不断累积!等到了一定程度的时候对于与Java多线程相关的东西就很难理解,今天需要探讨的 ...

  2. Java多线程相关面试题及答案-整理

    1.什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速.比如,如果一个线程完成 ...

  3. Java[3] Java多线程相关资料

    Java多线程: http://www.uml.org.cn/j2ee/201509093.asp

  4. Java多线程同步代码块

    /*多线程的安全问题1.为什么会出现安全问题?因为程序在运行时,会出现一个线程在判断条件满足后,具备了执行资格,但没有运行代码后一个线程也判断了条件,也具备了执行资格,后一个线程运行了代码,但这时候, ...

  5. 2018/1/15 JAVA多线程相关

    本文不说synchronized相关,它就是JAVA的一个保留关键字,jdk自己实现了它,但说真的,可应用场景真的少,相比lock接口,它还是被淘汰好吧; 首先,说说lock接口,lock接口是一个工 ...

  6. Java多线程相关的常用接口

    Runnable 是一个接口,里面只声明了一个方法run();返回值为void所以无法拿到执行完的结果.只能通过共享变量或者线程通信来搞定.Future就是对具体的Runable或者Callable任 ...

  7. Java多线程相关知识

    1)wait()  notify()  sleep() sleep是Thread类的函数,wait和notify是Object的函数. sleep的时候keep对象锁,wait的时候release 对 ...

  8. 复利计算C语言转java的相关代码

    static void principal()// 计算本金 { int N, m; double i, F, P; System.out.printf("复利终值:"); F = ...

  9. Java多线程相关的API方法以及作用摘要

    wait() 会让当前运行线程 阻塞,并释放对应的对象锁, 一般由当前线程持有的对象锁调用 x.wait(): 当前线程必须拥有此对象的monitor(即锁),才能调用此对象的wait()方法能让当前 ...

随机推荐

  1. python第八课——random模块的使用

    2.2.如何获取随机整数值? 引入random模块的使用 randint(a,b)函数:作用:返回给程序一个[a,b]范围内的随机整数注意:含头含尾闭区间 思路步骤: 第一步:导入random模块到相 ...

  2. CSS3新特性2D、3D效果讲解

    希望这篇博客可以对你有所帮助,如果有什么技术上的问题,希望我们可以做进一步的交流,如果你觉得我哪里阐述的不正确或者你有更好的更透彻的理解,也可以联系我,我在这里随时等着你. 对于css/html是每个 ...

  3. [SDOI2014]LIS

    这道题还是非常好的 首先第一问可以让我们联想到某网络流二十四题里的一道题,发现建图方式应该和这道题差不多啊 所以首先跑一遍\(dp\),求出\(dp[i]\)表示\(i\)位置结束的\(LIS\)长度 ...

  4. 【转】Android SDK,ADT,API 版本的对应关系

    写对应关系之前,先了解一下几个名字的含义. 一. Android ADT: 按照官方网站的开发介绍:Android Development Tools (ADT) is a plugin for th ...

  5. HBase学习之路 (一)HBase基础介绍

    产生背景 自 1970 年以来,关系数据库用于数据存储和维护有关问题的解决方案.大数据的出现后, 好多公司实现处理大数据并从中受益,并开始选择像 Hadoop 的解决方案.Hadoop 使用分 布式文 ...

  6. React-Native 之 index.android.bundle

    问题: index.android.bundle  这个bug 我相信很少同学会遇到,然而就是这个问题,困扰了我跟我的同事多天, 各种方法处理:  进入 android 目录  ./gradlew c ...

  7. 课后实践之mybash20155314

    课后实践之mybash 实践要求 加分题-mybash的实现 使用fork,exec,wait实现mybash 写出伪代码,产品代码和测试代码 发表知识理解,实现过程和问题解决的博客(包含代码托管链接 ...

  8. DOS命令行简单用法

    DOS命令行简单用法 基本用法 1.cd(回车)从当前目录切回到根目录. 2.dir(回车)列出当前目录下的所有文件. 3.md kkk(回车)在当前目录下创建一个名称为kkk的文件夹. 4.rd k ...

  9. TensorFlow入门:线性回归

    随机.mini-batch.batch(见最后解释) 在每个 epoch 送入单个数据点.这被称为随机梯度下降(stochastic gradient descent).我们也可以在每个 epoch ...

  10. 关于C#的静态类和静态构造函数

    静态构造函数是C#的一个新特性,其实好像很少用到.不过当我们想初始化一些静态变量的时候就需要用到它了.这个构造函数是属于类的,而不是属于哪里实例的,就是说这个构造函数只会被执行一次.也就是在创建第一个 ...